激动人心的 Angular HttpClient的源码解析

Angular 4.3.0-rc.0 版本已经发布🐦。在这个版本中,我们等到了一个令人兴奋的新功能 - HTTPClient API 的改进版本,以后妈妈再也不用担心我处理 HTTP 请求了😆。

HttpClient 是已有 Angular HTTP API 的演进,它在一个单独的 @angular/common/http 包中。这是为了确保现有的代码库可以缓慢迁移到新的 API。

接下来让我们开启 Angular 新版Http Client 之旅。

安装

首先,我们需要更新所有的包到 4.3.0-rc.0 版本。然后,我们需要在 AppModule 中导入 HttpClientModule 模块。具体如下:

rush:js;"> import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent ],imports: [ browserModule,HttpClientModule ],bootstrap: [AppComponent] }) export class AppModule { }

现在一切准备就绪。让我们来体验一下我们一直期待的三个新特性。

特性一 认 JSON 解析

现在 JSON 是认的数据格式,我们不需要再进行显式的解析。即我们不需要再使用以下代码

res.json()).subscribe(...)

现在我们可以这样写:

rush:js;"> http.get(url).subscribe(...)

特性二 支持拦截器 (Interceptors)

拦截器允许我们将中间件逻辑插入管线中。

请求拦截器 (Request Interceptor)

rush:js;"> import { HttpRequest,HttpHandler,HttpEvent } from '@angular/common/http';

@Injectable()
class JWTInterceptor implements HttpInterceptor {

constructor(private userService: UserService) {}

intercept(req: HttpRequest,next: HttpHandler): Observable<HttpEvent> {

const JWT = Bearer ${this.userService.getToken()};
req = req.clone({
setHeaders: {
Authorization: JWT
}
});
return next.handle(req);
}
}

如果我们想要注册新的拦截器 (interceptor),我们需要实现 HttpInterceptor 接口,然后实现该接口中的 intercept 方法。

,next: HttpHandler): Observable>; }

需要注意的是,请求对象和响应对象必须是不可修改的 (immutable)。因此,我们在返回请求对象前,我们需要克隆原始的请求对象。

next.handle(req) 方法使用新的请求对象,调用底层的 XHR 对象,并返回响应事件流。

响应拦截器 (Response Interceptor)

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:

特性三 进度事件 (Progress Events)

进度事件可以用于跟踪文件上传和下载。

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 枚举定义如下:

其实除了上面介绍三个新的功能之外,还有以下两个新的功能:

  1. 基于 Angular 内部测试框架的 Post-request verification 和 flush 功能
  2. 类型化,同步响应体访问,包括对 JSON 响应体类型的支持。

最后我们来通过 client_spec.ts 文件中的测试用例,来进一步感受一下上述的新特性。

其它特性

发送 GET 请求

{ 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);
});
});
});

发送 POST 请求

{ 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',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');
});
});

发送 JSONP 请求

{ 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'); }); });

参考资源

A Taste From The New Angular HTTP Client

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...