注册
web

这一次,放下axios,使用基于rxjs的响应式HTTP客户端

众所周知,在浏览器端和 Node.js 端使用最广泛的 HTTP 客户端为 axios 。想必大家都对它很熟悉,它是一个用于浏览器和 Node.js 的、基于 Promise 的 HTTP 客户端,但这次的主角不是它。

起源

axios 的前身其实是 AngularJS$http 服务。

为了避免混淆,这里需要澄清一下:AngularJS 并不等于 AngularAngularJS 是特指 angular.js v1.x 版本,而 Angular 特指 angular v2+ (没有 .js)和其包含的一系列工具链。

这样说可能不太严谨,但 axios 深受 AngularJS 中提供的$http 服务的启发。归根结底,axios 是为了提供一个类似独立的服务,以便在 AngularJS 之外使用。

发展

但在 Angular 中,却没有继续沿用之前的 $http 服务,而是选择与 rxjs 深度结合,设计出了一个比 $http 服务更先进的、现代化的,响应式的 HTTP 客户端。 在这个响应式的 HTTP Client 中,发送请求后接收到的不再是一个 Promise ,而是来自 rxjsObservable,我们可以订阅它,从而侦听到请求的响应:

const observable = http.get('url');
observable.subscribe(o => console.log(o));

有关它的基本形态及详细用法,请参考官方文档

正文

@ngify/http 是一个形如 Angular HttpClient 的响应式 HTTP 客户端。@ngify/http的目标与 axios 相似:提供一个类似独立的服务,以便在 Angular 之外使用。

@ngify/http 提供了以下主要功能:

先决条件

在使用 @ngify/http 之前,您应该对以下内容有基本的了解:

  • JavaScript / TypeScript 编程。

  • HTTP 协议的用法。

  • RxJS Observable 相关技术和操作符。请参阅 Observables 指南。

API

有关完整的 API 定义,请访问 ngify.github.io/ngify.

可靠性

@ngify/http 使用且通过了 Angular HttpClient 的单元测试(测试代码根据 API 的细微差异做出了相应的更改)。

安装

npm i @ngify/http

基本用法

import { HttpClientHttpContextHttpContextTokenHttpHeadersHttpParams } from '@ngify/http';
import { filter } from 'rxjs';

const http = new HttpClient();

http.get<code: number, data: any, msg: string }>('url''k=v').pipe(
 filter(({ code }) => code === 0)
).subscribe(res => console.log(res));

http.post('url', { k'v' }).subscribe(res => console.log(res));

const HTTP_CACHE_TOKEN = new HttpContextToken(() => 1800000);

http.put('url'null, {
 contextnew HttpContext().set(HTTP_CACHE_TOKEN)
}).subscribe(res => console.log(res));

http.patch('url'null, {
 params: { k'v' }
}).subscribe(res => console.log(res));

http.delete('url'new HttpParams('k=v'), {
 headersnew HttpHeaders({ Authorization'token' })
}).subscribe(res => console.log(res));

拦截请求和响应

借助拦截机制,你可以声明一些拦截器,它们可以检查并转换从应用中发给服务器的 HTTP 请求。这些拦截器还可以在返回应用的途中检查和转换来自服务器的响应。多个拦截器构成了请求/响应处理器的双向链表。

@ngify/http 会按照您提供拦截器的顺序应用它们。

import { HttpClientHttpHandlerHttpRequestHttpEventHttpInterceptorHttpEventType } from '@ngify/http';
import { Observabletap } from 'rxjs';

const http = new HttpClient([
 new class implements HttpInterceptor {
   intercept(requestHttpRequest<unknown>nextHttpHandler): Observable<HttpEvent<unknown>> {
     // 克隆请求以修改请求参数
     request = request.clone({
       headersrequest.headers.set('Authorization''token')
    });

     return next.handle(request);
  }
},
{
   intercept(requestHttpRequest<unknown>nextHttpHandler) {
     request = request.clone({
       paramsrequest.params.set('k''v')
    });

     console.log('拦截后的请求'request);

     return next.handle(request).pipe(
       tap(response => {
         if (response.type === HttpEventType.Response) {
           console.log('拦截后的响应'response);
        }
      })
    );
  }
}
]);

虽然拦截器有能力改变请求和响应,但 HttpRequestHttpResponse 实例的属性是只读的,因此让它们基本上是不可变的。

有充足的理由把它们做成不可变对象:应用可能会重试发送很多次请求之后才能成功,这就意味着这个拦截器链表可能会多次重复处理同一个请求。 如果拦截器可以修改原始的请求对象,那么重试阶段的操作就会从修改过的请求开始,而不是原始请求。 而这种不可变性,可以确保这些拦截器在每次重试时看到的都是同样的原始请求。

如果你需要修改一个请求,请先将它克隆一份,修改这个克隆体后再把它传递给 next.handle()

替换 HTTP 请求类

@ngify/http 内置了以下 HTTP 请求类:

HTTP 请求类描述
HttpXhrBackend使用 XMLHttpRequest 进行 HTTP 请求
HttpFetchBackend使用 Fetch API 进行 HTTP 请求
HttpWxBackend微信小程序 中进行 HTTP 请求

默认使用 HttpXhrBackend,可以通过修改配置切换到其他的 HTTP 请求类:

import { HttpFetchBackendHttpWxBackendsetupConfig } from '@ngify/http';

setupConfig({
 backendnew HttpFetchBackend()
});

你还可使用自定义的 HttpBackend 实现类:

import { HttpBackendHttpClientHttpRequestHttpEventsetupConfig } from '@ngify/http';
import { Observable } from 'rxjs';

// 需要实现 HttpBackend 接口
class CustomHttpBackend implements HttpBackend {
 handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
   // ...
}
}

setupConfig({
 backendnew CustomHttpBackend()
});

如果需要为某个 HttpClient 单独配置 HttpBackend,可以在 HttpClient 构造方法中传入:

const http = new HttpClient(new CustomHttpBackend());

// 或者

const http = new HttpClient({
 interceptors: [/* 一些拦截器 */],
 backendnew CustomHttpBackend()
});

在 Node.js 中使用

@ngify/http 默认使用浏览器实现的 XMLHttpRequestFetch API。要在 Node.js 中使用,您需要进行以下步骤:

XMLHttpRequest

如果需要在 Node.js 环境下使用 XMLHttpRequest,可以使用 xhr2,它在 Node.js API 上实现了 W3C XMLHttpRequest 规范。
要使用 xhr2 ,您需要创建一个返回 XMLHttpRequest 实例的工厂函数,并将其作为参数传递给 HttpXhrBackend 构造函数:

import { HttpXhrBackendsetupConfig } from '@ngify/http';
import * as xhr2 from 'xhr2';

setupConfig({
 backendnew HttpXhrBackend(() => new xhr2.XMLHttpRequest())
});

Fetch API

如果需要在 Node.js 环境下使用 Fetch API,可以使用 node-fetchabort-controller
要应用它们,您需要分别将它们添加到 Node.jsglobal

import fetch from 'node-fetch';
import AbortController from 'abort-controller';
import { HttpFetchBackend, HttpWxBackend, setupConfig } from '@ngify/http';

global.fetch = fetch;
global.AbortController = AbortController;

setupConfig({
backend: new HttpFetchBackend()
});

传递额外参数

为保持 API 的统一,需要借助 HttpContext 来传递一些额外参数。

Fetch API 额外参数

import { HttpContext, FETCH_TOKEN } from '@ngify/http';

// ...

// Fetch API 允许跨域请求
http.get('url', null, {
context: new HttpContext().set(FETCH_TOKEN, {
mode: 'cors',
// ...
})
});

微信小程序额外参数

import { HttpContextWX_UPLOAD_FILE_TOKENWX_DOWNLOAD_FILE_TOKENWX_REQUSET_TOKEN } from '@ngify/http';

// ...

// 微信小程序开启 HTTP2
http.get('url'null, {
 contextnew HttpContext().set(WX_REQUSET_TOKEN, {
   enableHttp2true,
})
});

// 微信小程序文件上传
http.post('url'null, {
 contextnew HttpContext().set(WX_UPLOAD_FILE_TOKEN, {
   filePath'filePath',
   fileName'fileName'
})
});

// 微信小程序文件下载
http.get('url'null, {
 contextnew HttpContext().set(WX_DOWNLOAD_FILE_TOKEN, {
   filePath'filePath'
})
});

更多

有关更多用法,请访问 angular.cn

作者:Sisyphus
来源:juejin.cn/post/7079724273929027597

0 个评论

要回复文章请先登录注册