激动人心的 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 相关文章推荐
JavaScript 异步方法队列链实现代码分析
Jun 05 Javascript
鼠标右击事件代码(asp.net后台)
Jan 27 Javascript
基于jquery实现控制经纬度显示地图与卫星
May 20 Javascript
js获取客户端外网ip的简单实例
Nov 21 Javascript
jquery实现界面无刷新加载登陆注册
Jul 30 Javascript
Vue2.0实现购物车功能
Jun 05 Javascript
knockoutjs模板实现树形结构列表
Jul 31 Javascript
详解jquery选择器的原理
Aug 01 jQuery
Angular2 自定义表单验证器的实现方法
Dec 14 Javascript
angular6根据environments配置文件更改开发所需要的环境的方法
Mar 06 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
Apr 11 Javascript
关于vue-router-link选择样式设置
Apr 30 Vue.js
基于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构造函数实例讲解
2013/11/13 PHP
单台服务器的PHP进程之间实现共享内存的方法
2014/06/13 PHP
PHP实现中文圆形印章特效
2015/06/19 PHP
5款适合PHP使用的HTML编辑器推荐
2015/07/03 PHP
通过修改Laravel Auth使用salt和password进行认证用户详解
2017/08/17 PHP
PHP PDOStatement::rowCount讲解
2019/02/01 PHP
laravel通过a标签从视图向控制器实现传值
2019/10/15 PHP
Thinkphp 框架基础之源码获取、环境要求与目录结构分析
2020/04/27 PHP
JavaScript 空位补零实现代码
2010/02/26 Javascript
基于JQuery 的消息提示框效果代码
2011/07/31 Javascript
javascript 进阶篇2 CSS XML学习
2012/03/14 Javascript
Javascript中的delete介绍
2012/09/02 Javascript
js实现Select下拉框具有输入功能的方法
2015/02/06 Javascript
浅谈轻量级js模板引擎simplite
2015/02/13 Javascript
使用impress.js制作幻灯片
2015/09/09 Javascript
jquery UI Datepicker时间控件的使用方法(终结版)
2015/11/07 Javascript
JavaScript数据结构之二叉查找树的定义与表示方法
2017/04/12 Javascript
Vue 拦截器对token过期处理方法
2018/01/23 Javascript
Node.JS循环删除非空文件夹及子目录下的所有文件
2018/03/12 Javascript
JavaScript模板引擎应用场景及实现原理详解
2018/12/14 Javascript
nvm、nrm、npm 安装和使用详解(小结)
2019/01/17 Javascript
Vue触发隐藏input file的方法实例详解
2019/08/14 Javascript
python中for语句简单遍历数据的方法
2015/05/07 Python
python自动化脚本安装指定版本python环境详解
2017/09/14 Python
Python编程给numpy矩阵添加一列方法示例
2017/12/04 Python
python如何制作英文字典
2019/06/25 Python
在 Jupyter 中重新导入特定的 Python 文件(场景分析)
2019/10/27 Python
Python类继承和多态原理解析
2020/02/05 Python
python正则过滤字母、中文、数字及特殊字符方法详解
2020/02/11 Python
家长对老师的感言
2014/03/11 职场文书
金融管理专业毕业生求职信
2014/03/12 职场文书
优秀食品类广告词
2014/03/19 职场文书
法定代表人授权委托书
2014/09/19 职场文书
2014年纳税评估工作总结
2014/12/23 职场文书
网络营销实训总结
2015/08/03 职场文书
分析SQL窗口函数之排名窗口函数
2022/04/21 Oracle