使用react框架中经常使用到umi来搭建管理我们的项目,其中涉及到请求的模块,umi自身提供了 umi-request 库 方便了我们做网络请求, umi-request的官方文档可见其githubREADME.MD文件, 大部分功能都在readme中查询,大部分功能已经将的很清楚了,单独拿出 middleware
和 interceptors
这两个概念讲一下。
middleware
和 interceptors
在使用umi-request的过程中最常见的需求是,对网络请求的拦截,在请求前或请求后做一些事情,例如URL添加前缀,过滤无效参数,上报接口错误,页面错误统一处理,单独接口定制化处理等..., middleware
(中间件) 和 interceptors
(拦截器) 以及 errorHandler 是实现此类需求绕不过去的概念,虽然他们都能影响请求和返回结果,但是哪种场景下使用哪个函数?需要了解其实如何实现的,以及代码的前后执行逻辑。
// onion/index.js
execute(params = null) {
const fn = compose([
...this.middlewares,
...this.defaultMiddlewares,
...Onion.globalMiddlewares,
...Onion.coreMiddlewares,
]);
return fn(params);
}
使用
调用逻辑:extend(configObj)生成request实例,request初始化中生成一个Core实例,Core实例初始化中使用一个Onion实例, 我们调用的request.use实际上是Onion实例上的use方法
use方法的第二个参数是可选参数,根据这个配置决定这个middleware加载到哪一层,调用的顺序也是不一样的,具体查看官方给的示例,
request.use(fn) 默认加载到 onion实例上,执行的时候会首先进入这一类的middleware
request.use(fn, { defaultInstance: true }) 官方给出的描述是 “默认实例中间件,供开发者使用”,此类middleware还是会加载到Onion实例上,默认Core实例中给出了"[]"的配置
request.use(fn, { global: true }) ,全局中间件中本身就内置了三个函数 [simplePost, simpleGet, parseResponseMiddleware]
'Content-Type': 'application/json;charset=UTF-8'
自动配置form类型的请求头; 功能在next()前;copy.status >= 200 && copy.status < 300
之外的code会作为错误被抛出; 功能在next()之后;request.use(fn, { core: true }), 核心中间件内置了一个函数 [fetchMiddleware]
, 也是umi-request真正发起接口调用的地方,使用的是浏览器原生的fetch对象
fetchMiddleware 实现了浏览器兼容性判断,缓存数据获取,超时逻辑,取消请求功能;在原生fetch请求回的第一时间,就进入了responseInterceptors的函数,所以interceptors.response.use进来的拦截器函数是第一批接触到真正fetch返回的response对象的函数,此时的response 对象是没有经过包装的原生fetch请求回来的对象,对象上常用的属性有status
, statusText
, ok
等属性
return new Promise((resolve, reject) => {
this.dealRequestInterceptors(obj) // 在此处完成了interceptors.request.use中函数的执行
.then(() => onion.execute(obj))
.then(() => {
resolve(obj.res);
})
.catch(error => {
...
});
const obj = {
req: { url, options },
res: null,
cache: this.mapCache,
responseInterceptors: [...Core.responseInterceptors, ...this.instanceResponseInterceptors],
};
request.use(async (ctx, next) => {
console.log('instanceA1');
await next();
console.log('instanceA2');
});
request.use(async (ctx, next) => {
console.log('instanceB1');
await next();
console.log('instanceB2');
});
request.use(
async (ctx, next) => {
console.log('globalA1');
await next();
console.log('globalA2');
},
{ global: true }
);
request.use(
async (ctx, next) => {
console.log('coreA1');
await next();
console.log('coreA2');
},
{ core: true }
);
// 执行顺序是
instanceA1 -> instanceB1 -> globalA1 -> coreA1 -> coreA2 -> globalA2 -> instanceB2 -> instanceA2
再加上我们上面的理解可以理解成这样
// 看一下fetch的位置
instanceA1 -> instanceB1 -> globalA1 -> coreA1 -> fetch() -> coreA2 -> globalA2 -> instanceB2 -> instanceA2
// 看一下内置中间件 + 拦截器的执行顺序
requestInterceptor -> 自定义middleware -> simplePost-> simpleGet-> parseResponseMiddleware -> fetch -> responseInterceptor
自定义middleware <- simplePost <- simpleGet <- parseResponseMiddleware <-
虽然没有具体规定哪个函数应该做什么,但是知道执行顺序以后,就很方便我们拓展了
联系客服