閱讀 Angular 6/RxJS 最新教程,請訪問 前端修仙之路
Angular 4.3.0-rc.0 版本已經發布?。在這個版本中,咱們等到了一個使人興奮的新功能 - HTTPClient API 的改進版本,之後媽媽不再用擔憂我處理 HTTP 請求了?。前端
HttpClient 是已有 Angular HTTP API 的演進,它在一個單獨的
@angular/common/http
包中。這是爲了確保現有的代碼庫能夠緩慢遷移到新的 API。
接下來讓咱們開啓 Angular 新版 Http Client 之旅。git
首先,咱們須要更新全部的包到 4.3.0-rc.0
版本。而後,咱們須要在 AppModule
中導入 HttpClientModule
模塊。具體以下:github
import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule ], bootstrap: [AppComponent] }) export class AppModule { }
如今一切準備就緒。讓咱們來體驗一下咱們一直期待的三個新特性。typescript
如今 JSON 是默認的數據格式,咱們不須要再進行顯式的解析。即咱們不須要再使用如下代碼:json
http.get(url).map(res => res.json()).subscribe(...)
如今咱們能夠這樣寫:bootstrap
http.get(url).subscribe(...)
攔截器容許咱們將中間件邏輯插入管線中。後端
import { HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; @Injectable() class JWTInterceptor implements HttpInterceptor { constructor(private userService: UserService) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const JWT = `Bearer ${this.userService.getToken()}`; req = req.clone({ setHeaders: { Authorization: JWT } }); return next.handle(req); } }
若是咱們想要註冊新的攔截器 (interceptor),咱們須要實現 HttpInterceptor
接口,而後實現該接口中的 intercept
方法。框架
export interface HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>; }
須要注意的是,請求對象和響應對象必須是不可修改的 (immutable)。所以,咱們在返回請求對象前,咱們須要克隆原始的請求對象。ide
next.handle(req)
方法使用新的請求對象,調用底層的 XHR 對象,並返回響應事件流。函數
@Injectable() class JWTInterceptor implements HttpInterceptor { constructor(private router: Router) {} intercept(req: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> { return next.handle(req).map(event => { if (event instanceof HttpResponse) { if (event.status === 401) { // JWT expired, go to login } } return event; } } }
響應攔截器能夠經過在 next.handle(req)
返回的流對象 (即 Observable 對象) 上應用附加的 Rx 操做符來轉換響應事件流對象。
接下來要應用 JWTInterceptor
響應攔截器的最後一件事是註冊該攔截器,即便用 HTTP_INTERCEPTORS
做爲 token,註冊 multi Provider:
[{ provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true }]
進度事件能夠用於跟蹤文件上傳和下載。
import { HttpEventType, HttpClient, HttpRequest } from '@angular/common/http'; http.request(new HttpRequest( 'POST', URL, body, { reportProgress: true })).subscribe(event => { if (event.type === HttpEventType.DownloadProgress) { // { // loaded:11, // Number of bytes uploaded or downloaded. // total :11 // Total number of bytes to upload or download // } } if (event.type === HttpEventType.UploadProgress) { // { // loaded:11, // Number of bytes uploaded or downloaded. // total :11 // Total number of bytes to upload or download // } } if (event.type === HttpEventType.Response) { console.log(event.body); } })
若是咱們想要跟蹤文件上傳或下載的進度,在建立請求對象時,咱們須要配置 {reportProgress: true}
參數。
此外在回調函數中,咱們經過 event.type
來判斷不一樣的事件類型,從進行相應的事件處理。
HttpEventType
枚舉定義以下:
export enum HttpEventType { /** * 表示請求已經被髮送 */ Sent, /** * 已接收到上傳進度事件 */ UploadProgress, /** * 已接收到響應狀態碼和響應頭 */ ResponseHeader, /** * 已接收到下載進度事件 */ DownloadProgress, /** * 已接收所有響應,包含響應體 */ Response, /** * 用戶自定義事件,來自攔截器或後端 */ User, }
其實除了上面介紹三個新的功能以外,還有如下兩個新的功能:
Post-request verification
和 flush
功能最後咱們來經過 client_spec.ts 文件中的測試用例,來進一步感覺一下上述的新特性。
describe('HttpClient', () => { let client: HttpClient = null !; let backend: HttpClientTestingBackend = null !; beforeEach(() => { backend = new HttpClientTestingBackend(); client = new HttpClient(backend); }); afterEach(() => { backend.verify(); }); // 請求驗證 describe('makes a basic request', () => { it('for JSON data', (done: DoneFn) => { client.get('/test').subscribe(res => { expect((res as any)['data']).toEqual('hello world'); done(); }); backend.expectOne('/test').flush({'data': 'hello world'}); }); it('for an arraybuffer', (done: DoneFn) => { const body = new ArrayBuffer(4); // 還支持 {responseType: 'text'}、{responseType: 'blob'} client.get('/test', {responseType: 'arraybuffer'}).subscribe(res => { expect(res).toBe(body); done(); }); backend.expectOne('/test').flush(body); }); it('that returns a response', (done: DoneFn) => { const body = {'data': 'hello world'}; client.get('/test', {observe: 'response'}).subscribe(res => { expect(res instanceof HttpResponse).toBe(true); expect(res.body).toBe(body); done(); }); backend.expectOne('/test').flush(body); }); }); });
describe('makes a POST request', () => { it('with text data', (done: DoneFn) => { client.post('/test', 'text body', {observe: 'response', responseType: 'text'}) .subscribe(res => { expect(res.ok).toBeTruthy(); expect(res.status).toBe(200); done(); }); backend.expectOne('/test').flush('hello world'); }); it('with json data', (done: DoneFn) => { const body = {data: 'json body'}; client.post('/test', body, {observe: 'response', responseType: 'text'}).subscribe(res => { expect(res.ok).toBeTruthy(); expect(res.status).toBe(200); done(); }); const testReq = backend.expectOne('/test'); expect(testReq.request.body).toBe(body); testReq.flush('hello world'); }); });
describe('makes a JSONP request', () => { it('with properly set method and callback', (done: DoneFn) => { client.jsonp('/test', 'myCallback').subscribe(() => done()); backend.expectOne({method: 'JSONP', url: '/test?myCallback=JSONP_CALLBACK'}) .flush('hello world'); }); });