@angular/common/http
中的HttpClient
類,Angular 爲應用程序提供了一個簡化的 API 來實現 HTTP 功能。它基於瀏覽器提供的XMLHttpRequest
接口。 HttpClient
帶來的其它優勢包括:可測試性、強類型的請求和響應對象、發起請求與接收響應時的攔截器支持,以及更好的、基於可觀察(Observable)對象的錯誤處理機制。javascript
import {HttpClientModule} from '@angular/common/http';
imports: [ BrowserModule, // Include it under 'imports' in your application module // after BrowserModule. HttpClientModule, ],
{ "results": [ "Item 1", "Item 2", ] }
//HttpClient的get()方法可讓訪問此數據的代碼很是直白: this.http.get('/api/items').subscribe(data => { // Read the result field from the JSON response. this.results = data['results']; (寫成,TypeScript 就會抱怨說來自HTTP的沒有一個名叫的屬性) }); data.resultsObjectresults
上面的例子中,訪問data['results']
是用方括號語法來取得results字段的。若是寫成data.results
,TypeScript 就會抱怨說來自HTTP的Object
沒有一個名叫results
的屬性。 那是由於HttpClient
把 JSON 格式的響應體解析成了一個Object
,它並不知道這個對象的形態應該是什麼。java
其實能夠告訴HttpClient
這個響應體應該是什麼類型的,並且這是推薦的作法。 要這樣作,首先咱們要定義一個接口來描述這個類型的正確形態:json
interface ItemsResponse { results: string[]; } http.get<ItemsResponse>('/api/items').subscribe(data => { // data is now an instance of type ItemsResponse, so you can do this: this.results = data.results; });
經過observe
選項來告訴HttpClient
,你想要完整的響應信息,而不是隻有響應體:後端
http
.get<MyJsonData>('/data.json', {observe: 'response'})
.subscribe(resp => {
console.log(resp.headers.get('X-Custom-Header'))
console.log(resp.body.someField);
});
http
.get<ItemsResponse>('/api/items')
.subscribe(
// Successful responses call the first callback.
data => {...},
// Errors will call this callback instead:
err => {
console.log('Something went wrong!');
}
);
.retry()
操做符這種策略對於那些臨時性的並且不大可能重複發生的錯誤會頗有用api
http
.get<ItemsResponse>('/api/items')
// Retry this request up to 3 times.
.retry(3)
// Any errors after the 3rd retry will fall through to the app.
.subscribe(...);
http .get('/textfile.txt', {responseType: 'text'}) .subscribe(data => console.log(data));
const body = {name: 'Brad'}; http.post('/api/developers/add', body).subscribe(...);
注意這個subscribe()
方法。 全部從HttpClient
返回的可觀察對象都是冷的(cold),也就是說,它們只是發起請求的藍圖而已。在咱們調用subscribe()
以前,什麼都不會發生,而當咱們每次調用subscribe()
時,就會獨立發起一次請求。 好比,下列代碼會使用一樣的數據發送兩次一樣的 POST 請求:數組
const req = http.post('/api/items/add', body);
// 0 requests made - .subscribe() not called.
req.subscribe();
// 1 request made.
req.subscribe();
// 2 requests made
除了 URL 和可能的請求體以外,要發送的請求中你可能還但願配置一些別的東西。全部這些均可以經過給此次請求傳一個額外的options
(選項)對象來解決瀏覽器
http .post('/api/items/add', body, { headers: new HttpHeaders().set('Authorization', 'my-auth-token'), }) .subscribe();
HttpHeaders
類是不可變對象(immutable),因此每一個set()
都會返回一個新實例,而且應用上這些修改緩存
http .post('/api/items/add', body, { params: new HttpParams().set('id', '3'), }) .subscribe();
這種狀況下,咱們會往 URL /api/items/add?id=3
上發送一個 POST 請求服務器
@angular/common/http
的主要特性之一是攔截器,它能聲明一些攔截器,攔在應用和後端之間。當應用程序發起一個請求時,攔截器能夠在請求被髮往服務器以前先轉換這個請求。而且在應用看到服務器發回來的響應以前,轉換這個響應。這對於處理包括認證和記錄日誌在內的一系列工做都很是有用。app
要實現一個攔截器,就要聲明一個實現了HttpInterceptor
接口的類,它只有一個intercept()
方法。下面是一個最簡單的攔截器,它什麼也不作,只是簡單的轉發請求而不作任何修改:
import {Injectable} from '@angular/core'; import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http'; @Injectable() export class NoopInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req); } }
intercept
是一個方法,它把一個請求對象轉換成一個返回這個響應的可觀察對象(Observable)
固然,大多數時候,攔截器會對請求作一些小的修改,而後才把它轉給攔截器鏈中的其它部分,也就是所傳進來的next
參數。next
是一個HttpHandler
,是一個相似於intercept
的接口,它會把一個請求對象轉換成一個可觀察的響應對象。在攔截器中,next
老是表明位於攔截器鏈中的下一個攔截器(若是有的話),若是沒有更多攔截器了,它就會是最終的後端。因此,大多數攔截器的最後一句都會以它們轉換後請求對象爲參數調用next.handle
函數。
像上面這樣簡單地聲明NoopInterceptor
並不會讓咱們的應用實際使用它。還要經過把它做爲攔截器提供給咱們的應用模塊纔會生效,代碼以下:
import {NgModule} from '@angular/core';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
@NgModule({
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: NoopInterceptor,
multi: true,
}],
})
export class AppModule {}
注意multi: true
選項。這是必須的,由於它會告訴 Angular 這個 HTTP_INTERCEPTORS
表示的是一個數組,而不是單個的值。
當咱們在一個應用中提供了多個攔截器時,Angular 會按照你提供時的順序應用它們(譯註:即模塊的providers
數組中列出的順序)。
攔截器要檢查和修改準備發出的請求和接收進來的響應。可是,你可能會驚奇的發現HttpRequest
和HttpResponse
類在很大程度上倒是不可變的。
由於應用可能會重發請求,而攔截器鏈可能會屢次處理同一個請求。若是請求是可變的,每次重試時的請求均可能和原始的請求不同。而不可變對象能夠確保攔截器每次重試時處理的都是同一個請求。
若是確實須要修改請求體,咱們就得本身複製它,修改這個複本,而後使用clone()
來複制這個請求,並使用這個新的請求體。
因爲請求都是不可變的,因此不能直接修改它們。要想修改,就使用clone()
函數:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // This is a duplicate. It is exactly the same as the original. const dupReq = req.clone(); const secureReq = req.clone({url: req.url.replace('http://', 'https://')}); }
攔截器的常見用途之一是爲所發出的請求設置默認的請求頭。好比,假設咱們有一個可注入的AuthService
,它能夠提供一個認證令牌,而咱們但願寫一個攔截器,它負責把這個令牌添加到全部要發出的請求中:
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authHeader = this.auth.getAuthorizationHeader();
const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
//簡寫: const authReq = req.clone({setHeaders: {Authorization: authHeader}});
return next.handle(authReq);
}
}
這種能夠修改頭的攔截器能夠用於不少不一樣的操做,好比:
認證 / 受權
控制緩存行爲。好比If-Modified-Since
XSRF 防禦