欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-14 19:31:58
angular 版本已经发布????。在这个版本中,我们等到了一个令人兴奋的新功能 - httpclient api 的改进版本,以后妈妈再也不用担心我处理 ht...

angular 版本已经发布????。在这个版本中,我们等到了一个令人兴奋的新功能 - 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

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