0.0.6 • Published 4 years ago
@retailwe/common-libs-http-client v0.0.6
接口代理 HttpClient
背景
微信小程序的
wx.request
太单薄- callback 的调用方式,容易造成 callback hell。
- get 请求和 post 请求,需要自行处理参数传递方式。
- 返回结果较底层,在业务开发中,对基本的 http status 需要手动处理。
- 实际业务开发中,无法满足对网关层的切面编程。
fly.js 能对
wx.request
进行更高一级的包装,提供了 Promise 链式调用,拦截器等功能,但是存在以下问题:- 拦截器只支持单例,且是静态时,如果网关层逻辑复杂,会造成代码耦合严重。
- 使用方式还不足够简便,实际业务开发中,每次使用都需要引入 api url constant,和 fly 实例。
- 对于企业级应用,还有很多的工作要做。
在实际的企业级业务开发中,往往会有:鉴权、接口日志、接口协议、状态码处理、接口缓存、业务接口集中管理等通用场景需要满足。
功能与设计
针对 wx.reqeust
和 fly.js
的局限性,结合通用的企业级开发需要,通过设计一层「接口代理层 HttpClient」来消化业务接口与业务逻辑之间的问题。业务只需关心两个事情:
- 请求哪个接口,入参是什么。
- 接口返回业务数据是什么。
目标
- 内核由 fly.js 驱动
- 支持串行异步拦截器,满足对网关层的切面编程需要,提供更好的代码解耦能力。
- 提供的业务接口的集中管理
- 内建常用拦截器:
- 头部注入
- 请求体注入
- 动态 baseURL
- Http 日志打印
- (TODO) Http 状态码处理
- (TODO) 接口数据缓存
- (TODO) 请求阻断问题
实现拦截器异步串行
启发于 Koa 的洋葱模型:
我们期望一个拦截器的逻辑能满足单一原则,例如一个缓存拦截器:
- 在 request 阶段,它能够先读取缓存,返回结果。
- 在 resposne 阶段,它能够把接口数据缓存起来,供下一次使用。
这是一个高度解耦的逻辑,封装这部分逻辑,是一个很常见的需求。
所以,我们期望它能够可插拔式的使用,例如:
httpClient.registerInterceptors({
request: req => {
// 读取缓存,返回结果
},
response: res => {
// 数据缓存起来
}
});
RetailWe HttpClient 由 fly.js 内核驱动,实现了多个拦截器的串行,同时支持异步。
内部维护了两个队列:
- 全局拦截器:为所有 api 接口配置拦截器
- api 级拦截器:可以为单个业务接口配置拦截器
执行顺序:api request → 全局 request → 全局 response → api response
安装
npm install @retailwe/common-libs-http-client
使用
1. 快速使用
import { HttpClient } from '@retailwe/common-libs-http-client';
const httpClient = new HttpClient({
config: {
baseURL: 'https://myapi.com'
}
});
// 注册 API
const getUser = httpClient.registerApi({
url: '/user/get',
config: {
method: 'post'
}
});
// 发起请求
const res = await getUser({ name: 'jc' });
2. 注册 API
静态注册 API
// 注册 API,返回 api function const getUser = httpClient.registerApi({ url: '/user/get', config: { method: 'post' } }); // 发起请求 const res = await getUser({ name: 'jc' });
动态注册 API
使用
httpClient.fetch()
,会自动判断 API 是否已注册- 已注册,取出 api,发起请求。
- 未注册,会先注册,保存,然后再发起请求。
const res = await httpClient.fetch({ url: '/user/get', config: { method: 'post' } }); // 简易写法 const res = await httpClient.fetch('/user/get', { name: 'jc' });
3. 注册拦截器
使用拦截器之前,建议先搞懂 flyjs-interceptor
注册全局拦截器
const httpClient = new HttpClient({ interceptors: [ { request: req => console.log('in request'), response: res => console.log('in response'), responseErr: err => console.log('in responseErr') } ] });
动态注册拦截器
httpClient.registerInterceptor({ request: req => console.log('in request'), response: res => console.log('in response'), responseErr: err => console.log('in responseErr') });
注册 API 拦截器
const getUser = httpClient.registerApi({ url: '/user/get', interceptors: [ { request: req => console.log('in request'), response: res => console.log('in response'), responseErr: err => console.log('in responseErr') } ] });
注册多个拦截器
以上 interceptors 类型是数组,支持传入多个拦截器,拦截器会串行执行,例如:
const httpClient = new HttpClient({ interceptors: [ { request: req => console.log('in request A'), response: res => console.log('in response A'), responseErr: err => console.log('in responseErr A') }, { request: req => console.log('in request B'), response: res => console.log('in response B'), responseErr: err => console.log('in responseErr B') } ] });
4. 配置
配置继承于 flyio 的配置:flyio-请求配置,类型定义:
export interface IFlyConfig {
// 请求方法,默认 get
method?: string;
// http请求头
headers?: object;
// 请求基地址
baseURL?: string;
// 超时时间,为0时则无超时限制
timeout?: number;
// 是否自动将Content-Type为“application/json”的响应数据转化为JSON对象,默认为true
parseJson?: boolean;
// 支持自定义参数
[propName: string]: any;
}
全局配置
const httpClient = new HttpClient({ config: {...}, });
API 配置
httpClient.registerApi({ url: '/user/get', config: {...}, })
运行时配置
静态 API
const getUser = httpClient.registerApi({ url: '/user/get', config: {...} }); const res = await getUser({ name: 'jc' }, config);
动态 API
const res = await httpClient.fetch('/user/get', { name: 'jc' }, config);
5. 内建拦截器
HttpClient 提供了常用的内建拦截器:
import { interceptors, HttpClient } from '@retailwe/common-libs-http-client';
const httpClient = new HttpClient({
interceptors: [{
// 动态设置 baseURL,支持异步
interceptors.baseURL(async () => 'https://myapi.com'),
// 动态注入请求头,支持异步
interceptors.injectReqHeaders(async () => ({ token: 'abc' })),
// 动态注入请求体,支持异步
interceptors.injectReqBody(async () => ({ token: 'abc' })),
// 日志打印
interceptors.httpLogger();
}]
})
6. TS 类型
提供内建的 TS 类型定义,如创建自定义拦截器:
import { IInterceptor } from '@retailwe/common-libs-http-client';
export default function genInterceptor(): IInterceptor {
return {
name: 'custemInterceptor',
request: async res => {...},
response: async res => {...},
responseError: async res => {...},
};
}
更多类型,见 API 文档:API