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

Angular学习笔记——Http(二) - 图1

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。

定义如下

  1. // HttpInterceptor
  2. export interface HttpInterceptor {
  3. intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>;
  4. }
  5. // InjectToken
  6. export const HTTP_INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('HTTP_INTERCEPTORS');

HttpHandler

HttpHandler 是整个 HttpClientModule 的核心,它定义了如何处理请求的函数 handle()。处理真实请求的 XhrBackend ,处理拦截器的 HttpInterceptingHandler 以及 包装了 HttpInterceptor 的 HttpInterceptorHandler , 都实现了这个接口。它是一个 “装饰器模式” 的应用。

定义如下

  1. // HttpHandler
  2. export abstract class HttpHandler {
  3. abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
  4. }

HttpInterceptorHandler

HttpInterceptorHandler 为 HttpHandler 的实现,它将 HttpInterceptor 与一个 HttpHandler 的实现,绑定成为一个新的 HttpHandler 对象,

定义如下

  1. // HttpInterceptor
  2. // 不可被注入
  3. export class HttpInterceptorHandler implements HttpHandler {
  4. // 需要一个实现了HttpHandler接口的对象 与一个实现了HttpInterceptor对象
  5. constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
  6. handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
  7. // 代理interceptor的调用
  8. return this.interceptor.intercept(req, this.next);
  9. }
  10. }

HttpInterceptingHandler

HttpInterceptingHandler 是可被注入的类型,它通过 Injector 获取我们注入的 HTTP_INTERCEPTORS 数组。通过 reduceRight 将 HttpInterceptor 与上一个 HttpHandler 包装为一个 HttpIntecepotorHandler 对象,最后返回一个 HttpInterceptor Handler 对象,以实现链式调用。

由于使用的是 reduceRight,并且默认提供的 HttpHandler 为 HttpBackend 的实现,因此我们的 BackEnd,在请求的管道里 会被最后一个调用。

定义如下

  1. // 这个可以被注入 对应的InjectToken为 HttpHandler
  2. @Injectable()
  3. export class HttpInterceptingHandler implements HttpHandler {
  4. // 调用链
  5. private chain: HttpHandler|null = null;
  6. // 注入了HttpBackend,以及Injector,方便我们动态拿HTTP_INTERCEPTORS
  7. constructor(private backend: HttpBackend, private injector: Injector) {}
  8. handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
  9. if (this.chain === null) {
  10. const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
  11. // 对INTERCEPTORS做Reduce操作,包装为HttpInterceptorHandler
  12. this.chain = interceptors.reduceRight(
  13. (next, interceptor) => new HttpInterceptorHandler(next, interceptor), this.backend);
  14. }
  15. // 返回调用链处理后的结果
  16. return this.chain.handle(req);
  17. }
  18. }

HttpBackend 与 XhrHttpBackend

HttpBackend 与 HttpHandler 签名相同,他存在的意义仅仅是提供了不同的 InjectToken,避免循环引用。

XhrHttpBackend 为 HttpBackend 的实现,它封装了 XMLHttpRequest 对象,提供了 Ajax 请求的实现。

  1. // HttpBackend
  2. export abstract class HttpBackend implements HttpHandler {
  3. abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
  4. }

HttpClient

这个就是我们经常注入的 Http 客户端了,它依赖注入了 HttpHandler 的实现,也就是我们的 HttpInterceptingHandler。提供了我们常用的 request、get、post 等接口,并且将拿到的 request 对象转交给 handler 进行处理。

HttpModule

Module 的内容就比较简单了,主要是管理了整个的依赖注入。

定义如下

  1. // HttpClientModule
  2. @NgModule({
  3. imports: [
  4. // 关于XSRF的实现自行看下源码
  5. HttpClientXsrfModule.withOptions({
  6. cookieName: 'XSRF-TOKEN',
  7. headerName: 'X-XSRF-TOKEN',
  8. }),
  9. ],
  10. providers: [
  11. HttpClient, //注入我们的HttpClient
  12. {provide: HttpHandler, useClass: HttpInterceptingHandler}, //为HttpHandler提供HttpInterceptingHandler的实现
  13. HttpXhrBackend,
  14. {provide: HttpBackend, useExisting: HttpXhrBackend}, //为HttpBackend提供 HttpXhrBackend的实现
  15. BrowserXhr,
  16. {provide: XhrFactory, useExisting: BrowserXhr}, // 提供XMLHttpRequest的实例,被XhrBackend依赖
  17. ],
  18. })
  19. export class HttpClientModule {
  20. }

在源码中我们可以看到 XMLHttpRequest 被拆到了 XhrFactory 中,以及 HttpBackend,HttpHandler 都是通过注入提供的实例,这是为了便于进行单元测试时的替换。 同时我们也可以通过注入自己的 HttpHandler 与 HttpBackend,替换掉默认的实现。

一个例子

最后,我们自己实现了一个基于 fetch 接口的简单 HttpClient,例子

实现内容如下

  • 通过 fetch 进行 Http 请求,仅 request
  • 简单的 HttpEvent (Sent , Response)
  • Interceptor 实现