激动人心的 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转换字符串为dom对象(字符串动态创建dom)
May 10 Javascript
JavaScript.The.Good.Parts阅读笔记(二)作用域&amp;闭包&amp;减缓全局空间污染
Nov 16 Javascript
JQuery入门——移除绑定事件unbind方法概述及应用
Feb 05 Javascript
node.js实现逐行读取文件内容的代码
Jun 27 Javascript
javascript实现iframe框架延时加载的方法
Oct 30 Javascript
使用js获取地址栏参数的方法推荐(超级简单)
Jun 14 Javascript
jQuery联动日历的实例解析
Dec 02 Javascript
详解JavaScript RegExp对象
Feb 04 Javascript
javascript性能优化之分时函数的介绍
Mar 28 Javascript
vue ssr 指南详读
Jun 29 Javascript
详解超简单的react服务器渲染(ssr)入坑指南
Feb 28 Javascript
如何通过JS实现转码与解码
Feb 21 Javascript
基于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
discuz程序的PHP加密函数原理分析
2011/08/05 PHP
PHP面向对象之后期静态绑定功能介绍
2015/05/18 PHP
php获取图片信息的方法详解
2015/12/10 PHP
php cookie 详解使用实例
2016/11/03 PHP
浅谈laravel 5.6 安装 windows上使用composer的安装过程
2019/10/18 PHP
PHP pthreads v3下同步处理synchronized用法示例
2020/02/21 PHP
PHP如何解决微信文章图片防盗链
2020/12/09 PHP
用JTrackBar实现的模拟苹果风格的滚动条
2007/08/06 Javascript
JQuery 表单中textarea字数限制实现代码
2009/12/07 Javascript
JavaScript高级程序设计 DOM学习笔记
2011/09/10 Javascript
JavaScript去除空格的三种方法(正则/传参函数/trim)
2013/02/06 Javascript
在for循环中length值是否需要缓存
2015/07/27 Javascript
基于javascript简单实现对身份证校验
2021/01/25 Javascript
JS/jQ实现免费获取手机验证码倒计时效果
2016/06/13 Javascript
ECMAScript6轮播图实践知识总结
2016/08/17 Javascript
Vue2.0基于vue-cli+webpack同级组件之间的通信教程(推荐)
2017/09/14 Javascript
JavaScript回调函数callback用法解析
2020/01/14 Javascript
bootstrap-closable-tab可实现关闭的tab标签页插件
2020/08/09 Javascript
python的正则表达式re模块的常用方法
2013/03/09 Python
Tornado Web服务器多进程启动的2个方法
2014/08/04 Python
Python爬虫之UserAgent的使用实例
2019/02/21 Python
python pyenv多版本管理工具的使用
2019/12/23 Python
Python matplotlib画曲线例题解析
2020/02/07 Python
瑞典时尚服装购物网站:Miinto.se
2017/10/30 全球购物
优质美利奴羊毛袜,不只是徒步旅行:Darn Tough Vermont
2018/11/05 全球购物
高中毕业自我鉴定
2013/12/19 职场文书
最新茶叶店创业计划书
2014/01/14 职场文书
金融系应届毕业生求职信
2014/05/26 职场文书
食品安全汇报材料
2014/08/18 职场文书
2014年业务员工作总结范文
2014/11/17 职场文书
阿凡达观后感
2015/06/10 职场文书
校园歌手大赛主持词
2015/07/03 职场文书
新课程改革心得体会
2016/01/22 职场文书
导游词之镇江焦山
2019/11/21 职场文书
Python+Appium自动化测试的实战
2021/06/30 Python
Mysql案例刨析事务隔离级别
2021/09/25 MySQL