上一篇文章,我们讲了 HttpClientModule 的应用,在这一篇我们一起来了解下 @angular/common/http 包中的源码,以及 HttpClientModule 的实现,在开始之前,我们先放一张图,说明一下关键类的继承&实现的关系。这里不包含依赖关系的说明。图片看不清可以保存为大图

HttpRequest
HttpRequest 为我们封装的请求对象,它所有的属性都是只读的,只能在构造函数中进行初始化。如果想改变一个 HttpRequest 对象的某个属性 我们需要调用 它的 clone()方法,并传递进去需要改变的属性值。
HttpResponse
看图我们可以发现 HttpResponseBase 有三个实现。
- HttpHeaderResponse (只包含 Header 内容的响应)
- HttpResponse(完整的响应 包含 Header 与 Body)
- HttpErrorResponse (错误的响应,通常对应的 HttpStatusCode 范围为 <100 && >=300)
其中 HttpHeaderResponse 与 HttpResponse 实现了 HttpEvent。
HttpEvent
Angular 将请求过程中可能发生的行为,为我们抽象出了 HttpEventType。它包含了如下几种类型
- Sent (请求已发送出去)
- UploadProgress (上传进度变化)
- DownloadProgress (下载进度变化)
- ResponseHeader (响应了头)
- Response (完整的响应)
- User (用户自定义事件)
这些 Event 都有自己接口定义,它们的接口都提供了 type:HttpEventType 这个属性,用于区分当前对象到底是哪种 EventType。其中 UploadProgressEvent 与 DownloadProgressEvent 都继承自 ProgressEvent 接口,提供了 total,loaded 属性帮助我们计算进度。 ResponseHeader 与 Response 继承自 HttpResponseBase,提供了标准的 Http 响应对象。
HttpEvent 是由 HttpSentEvent,HttpResponse,HttpHeaderResponse,HttpProgressEvent 与 HttpUserEvent 组合而成的类型。
HttpInterceptor
HttpInterceptor 是拦截器的定义,它只提供了 intercep()的签名。它的 InjectToken 是 HTTPS_INTERCEPTORS。
定义如下
// HttpInterceptorexport interface HttpInterceptor {intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>;}// InjectTokenexport const HTTP_INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('HTTP_INTERCEPTORS');
HttpHandler
HttpHandler 是整个 HttpClientModule 的核心,它定义了如何处理请求的函数 handle()。处理真实请求的 XhrBackend ,处理拦截器的 HttpInterceptingHandler 以及 包装了 HttpInterceptor 的 HttpInterceptorHandler , 都实现了这个接口。它是一个 “装饰器模式” 的应用。
定义如下
// HttpHandlerexport abstract class HttpHandler {abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;}
HttpInterceptorHandler
HttpInterceptorHandler 为 HttpHandler 的实现,它将 HttpInterceptor 与一个 HttpHandler 的实现,绑定成为一个新的 HttpHandler 对象,
定义如下
// HttpInterceptor// 不可被注入export class HttpInterceptorHandler implements HttpHandler {// 需要一个实现了HttpHandler接口的对象 与一个实现了HttpInterceptor对象constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {// 代理interceptor的调用return this.interceptor.intercept(req, this.next);}}
HttpInterceptingHandler
HttpInterceptingHandler 是可被注入的类型,它通过 Injector 获取我们注入的 HTTP_INTERCEPTORS 数组。通过 reduceRight 将 HttpInterceptor 与上一个 HttpHandler 包装为一个 HttpIntecepotorHandler 对象,最后返回一个 HttpInterceptor Handler 对象,以实现链式调用。
由于使用的是 reduceRight,并且默认提供的 HttpHandler 为 HttpBackend 的实现,因此我们的 BackEnd,在请求的管道里 会被最后一个调用。
定义如下
// 这个可以被注入 对应的InjectToken为 HttpHandler@Injectable()export class HttpInterceptingHandler implements HttpHandler {// 调用链private chain: HttpHandler|null = null;// 注入了HttpBackend,以及Injector,方便我们动态拿HTTP_INTERCEPTORSconstructor(private backend: HttpBackend, private injector: Injector) {}handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {if (this.chain === null) {const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);// 对INTERCEPTORS做Reduce操作,包装为HttpInterceptorHandlerthis.chain = interceptors.reduceRight((next, interceptor) => new HttpInterceptorHandler(next, interceptor), this.backend);}// 返回调用链处理后的结果return this.chain.handle(req);}}
HttpBackend 与 XhrHttpBackend
HttpBackend 与 HttpHandler 签名相同,他存在的意义仅仅是提供了不同的 InjectToken,避免循环引用。
XhrHttpBackend 为 HttpBackend 的实现,它封装了 XMLHttpRequest 对象,提供了 Ajax 请求的实现。
// HttpBackendexport abstract class HttpBackend implements HttpHandler {abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;}
HttpClient
这个就是我们经常注入的 Http 客户端了,它依赖注入了 HttpHandler 的实现,也就是我们的 HttpInterceptingHandler。提供了我们常用的 request、get、post 等接口,并且将拿到的 request 对象转交给 handler 进行处理。
HttpModule
Module 的内容就比较简单了,主要是管理了整个的依赖注入。
定义如下
// HttpClientModule@NgModule({imports: [// 关于XSRF的实现自行看下源码HttpClientXsrfModule.withOptions({cookieName: 'XSRF-TOKEN',headerName: 'X-XSRF-TOKEN',}),],providers: [HttpClient, //注入我们的HttpClient{provide: HttpHandler, useClass: HttpInterceptingHandler}, //为HttpHandler提供HttpInterceptingHandler的实现HttpXhrBackend,{provide: HttpBackend, useExisting: HttpXhrBackend}, //为HttpBackend提供 HttpXhrBackend的实现BrowserXhr,{provide: XhrFactory, useExisting: BrowserXhr}, // 提供XMLHttpRequest的实例,被XhrBackend依赖],})export class HttpClientModule {}
在源码中我们可以看到 XMLHttpRequest 被拆到了 XhrFactory 中,以及 HttpBackend,HttpHandler 都是通过注入提供的实例,这是为了便于进行单元测试时的替换。 同时我们也可以通过注入自己的 HttpHandler 与 HttpBackend,替换掉默认的实现。
一个例子
最后,我们自己实现了一个基于 fetch 接口的简单 HttpClient,例子。
实现内容如下
- 通过 fetch 进行 Http 请求,仅 request
- 简单的 HttpEvent (Sent , Response)
- Interceptor 实现
