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


Posted in Javascript onJuly 10, 2017

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 模块。具体如下:

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

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

特性一 默认 JSON 解析

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

http.get(url).map(res => res.json()).subscribe(...)

现在我们可以这样写:

http.get(url).subscribe(...)

特性二 支持拦截器 (Interceptors)

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

请求拦截器 (Request Interceptor)

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)。因此,我们在返回请求对象前,我们需要克隆原始的请求对象。

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

响应拦截器 (Response Interceptor)

@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 }]

特性三 进度事件 (Progress Events)

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

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,
}

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

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

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

其它特性

发送 GET 请求

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

发送 POST 请求

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

发送 JSONP 请求

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

参考资源

A Taste From The New Angular HTTP Client

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于JQuery制作的产品广告效果
Dec 08 Javascript
httpclient模拟登陆具体实现(使用js设置cookie)
Dec 11 Javascript
理解javascript异步编程
Jan 27 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(二)之数据支持json字符串、list集合
Aug 11 Javascript
谈谈JavaScript的New关键字
Aug 26 Javascript
Vuejs第十篇之vuejs父子组件通信
Sep 06 Javascript
基于Vue2的移动端开发环境搭建详解
Nov 03 Javascript
基于javascript实现数字英文验证码
Jan 25 Javascript
详解vue嵌套路由-params传递参数
May 23 Javascript
angularJs使用$watch和$filter过滤器制作搜索筛选实例
Jun 01 Javascript
javascript面向对象三大特征之继承实例详解
Jul 24 Javascript
详解webpack引用jquery(第三方模块)的三种办法
Aug 21 jQuery
基于JS实现仿京东搜索栏随滑动透明度渐变效果
Jul 10 #Javascript
jQuery实现QQ空间汉字转拼音功能示例
Jul 10 #jQuery
Underscore之Array_动力节点Java学院整理
Jul 10 #Javascript
Angular.js ng-file-upload结合springMVC的使用教程
Jul 10 #Javascript
underscore之Collections_动力节点Java学院整理
Jul 10 #Javascript
Angular.js组件之input mask对input输入进行格式化详解
Jul 10 #Javascript
underscore之Chaining_动力节点Java学院整理
Jul 10 #Javascript
You might like
利用php下载xls文件(自己动手写的)
2014/04/18 PHP
php异步:在php中使用fsockopen curl实现类似异步处理的功能方法
2016/12/10 PHP
用javascript实现给图片加链接
2007/08/15 Javascript
JS获取文本框,下拉框,单选框的值的简单实例
2014/02/26 Javascript
jquery实现导航固定顶部的效果仿蘑菇街
2014/10/22 Javascript
基于jQuery实现网页打印功能
2015/12/01 Javascript
js+div+css下拉导航菜单完整代码分享
2016/12/28 Javascript
jQuery实现弹出窗口弹出div层的实例代码
2017/01/09 Javascript
jQuery插件zTree实现的基本树与节点获取操作示例
2017/03/08 Javascript
angular4 如何在全局设置路由跳转动画的方法
2017/08/30 Javascript
Vue的Class与Style绑定的方法
2017/09/01 Javascript
vue2.0 和 animate.css的结合使用
2017/12/12 Javascript
bootstrap table支持高度百分比的实例代码
2018/02/28 Javascript
Spring Boot/VUE中路由传递参数的实现代码
2018/03/02 Javascript
微信web端后退强制刷新功能的实现代码
2018/03/04 Javascript
对Angular中单向数据流的深入理解
2018/03/31 Javascript
nodejs前端模板引擎swig入门详解
2018/05/15 NodeJs
vue使用自定义指令实现拖拽
2021/01/29 Javascript
python中多个装饰器的调用顺序详解
2019/07/16 Python
Python 使用多属性来进行排序
2019/09/01 Python
Django+uni-app实现数据通信中的请求跨域的示例代码
2019/10/12 Python
通过实例简单了解Python中yield的作用
2019/12/11 Python
Python list运算操作代码实例解析
2020/01/20 Python
python-sys.stdout作为默认函数参数的实现
2020/02/21 Python
python 实现性别识别
2020/11/21 Python
python实现excel公式格式化的示例代码
2020/12/23 Python
用python批量下载apk
2020/12/29 Python
Matlab使用Plot函数实现数据动态显示方法总结
2021/02/25 Python
css3个性化字体_动力节点Java学院整理
2017/07/12 HTML / CSS
25个CSS3动画按钮和菜单教程分享
2012/10/03 HTML / CSS
详解CSS3的图层阴影和文字阴影效果使用
2016/06/09 HTML / CSS
CSS3实现线性渐变用法示例代码详解
2020/08/07 HTML / CSS
Eastbay官网:美国最大的运动鞋网络零售商
2016/07/27 全球购物
21岁生日感言
2014/02/27 职场文书
怎样写离婚协议书
2014/09/10 职场文书
公司领导班子召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书