激动人心的 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 相关文章推荐
VBS通过WMI监视注册表变动的代码
Oct 27 Javascript
jQuery学习笔记 更改jQuery对象
Sep 19 Javascript
jQuery UI插件自定义confirm确认框的方法
Mar 20 Javascript
javascript图片预加载完整实例
Dec 10 Javascript
jquery validate表单验证的基本用法入门
Jan 18 Javascript
使用Object.defineProperty实现简单的js双向绑定
Apr 15 Javascript
JavaScript关联数组用法分析【概念、定义、遍历】
Mar 15 Javascript
在vue中v-bind使用三目运算符绑定class的实例
Sep 29 Javascript
vue+element-ui+axios实现图片上传
Aug 20 Javascript
node.js 如何监视文件变化
Sep 01 Javascript
基于vue实现微博三方登录流程解析
Nov 04 Javascript
原生JS实现音乐播放器
Jan 26 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
逆序二维数组插入一元素的php代码
2012/06/08 PHP
php实现天干地支计算器示例
2014/03/14 PHP
PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式
2014/06/12 PHP
php rsa加密解密使用详解
2015/01/14 PHP
php数组去除空值函数分享
2015/02/02 PHP
CI框架中数据库操作函数$this-&gt;db-&gt;where()相关用法总结
2016/05/17 PHP
如何正确配置Nginx + PHP
2016/07/15 PHP
php简单统计中文个数的方法
2016/09/30 PHP
谈谈php对接芝麻信用踩的坑
2016/12/01 PHP
JS中的public和private对象,即static修饰符
2012/01/18 Javascript
javascript读写json示例
2014/04/11 Javascript
JS+CSS实现简单的二级下拉导航菜单效果
2015/09/21 Javascript
Nodejs如何复制文件
2016/03/09 NodeJs
IntersectionObserver实现图片懒加载的示例
2017/09/29 Javascript
vue下拉列表功能实例代码
2018/04/08 Javascript
vue左侧菜单,树形图递归实现代码
2018/08/24 Javascript
微信小程序获取当前位置和城市名
2019/11/13 Javascript
Vue 请求传公共参数的操作
2020/07/31 Javascript
[02:59]DOTA2完美大师赛主赛事第三日精彩集锦
2017/11/25 DOTA
python基础教程之类class定义使用方法
2014/02/20 Python
Python操作MySQL模拟银行转账
2018/03/12 Python
python设计微型小说网站(基于Django+Bootstrap框架)
2019/07/08 Python
淘宝秒杀python脚本 扫码登录版
2019/09/19 Python
css3之UI元素状态伪类选择器实例演示
2017/08/11 HTML / CSS
css3教程之倾斜页面
2014/01/27 HTML / CSS
详解Canvas 实现炫丽的粒子运动效果(粒子生成文字)
2018/02/01 HTML / CSS
在HTML5 Canvas中放入图片和保存为图片的方法
2014/05/03 HTML / CSS
解析浏览器的一些“滚动”行为鉴赏
2019/09/16 HTML / CSS
澳大利亚在线生活方式商店:Mytopia
2018/07/08 全球购物
高三地理教学反思
2014/01/11 职场文书
中秋晚会活动方案
2014/08/31 职场文书
公司经营目标责任书
2015/01/29 职场文书
新闻发布会新闻稿
2015/07/17 职场文书
《有余数的除法》教学反思
2016/02/22 职场文书
Nginx反向代理学习实例教程
2021/10/24 Servers
css3 文字断裂效果
2022/04/22 HTML / CSS