共计 5347 个字符,预计需要花费 14 分钟才能阅读完成。
Eaxios 是基于 axios 封装的网络申请库,在放弃 API 与 axios 基本一致的状况下,简化服务端响应内容和各种异常情况的解决。
开发背景
如上图所示,是一次 Ajax 申请可能输入的后果,在前端咱们须要依据输入后果给用户不同的提醒。
- 申请被勾销:疏忽
- 网络异样:提醒查看是否连贯网络
- 申请超时:提醒网络慢,请切换网络
- 服务器异样:提醒零碎出问题了
- 响应解析失败:同上,且能够进行谬误日志上报
- 申请失败:这种状况通常是业务异样,前端须要依据错误码进行相应的解决,最简略的就是音讯揭示
- 申请胜利:前端拿到数据后更新界面
然而,现有的 Axios 库对于异样后果没有提供较好的封装,Axios Promise catch 里蕴含各种类型的谬误,而且没有提供错误码来辨认申请失败的起因。而且很多服务端接口会返回本人的错误码,这样在 Axios Promise then 里也须要解决业务异样。
此外,Axios 自身如下所述的一些问题和局限性。
- 如果设置 Axios responseType 为 json 时,服务端返回的非 JSON 格局的响应内容会因为无奈解析,response.data 为 null
对于 500 等谬误,响应内容会失落,所以不要去配置 responseType 为 json,对于使用者来说容易采到这个坑。
ps:尽管 Axios 官网文档申明 responseType 是 json,实际上底层调用 XMLHttpRequest 的 responseType 是没有传值的,应该是为了躲避这个问题。
- Axios 默认不论 HTTP 响应状态和 responseType 是什么,都会调用默认的 transformResponse
ps:应该是为了躲避上一个问题,默认提供了一个响应处理函数进行 JSON 解析,然而这会影响性能(500 等响应内容值较多时,会造成页面卡顿)。尽管 transformResponse 能够转换 response,理论接管到的参数是 response.data,所以无奈判断具体情况来决定是否进行解析 JSON。
- Axios then 和 catch 是依据 validateStatus 决定的,使用者解决以来较为麻烦。
现实状况下,使用者心愿 then 返回无效的数据,catch 返回各种谬误状况:申请被勾销、网络异样、网络超时、服务端异样、服务端数据格式谬误、业务异样。
- Axios 默认不解决
content-type
为application/x-www-form-urlencoded
类型的申请体,应用起来不够不便
优化计划:
- 如果设置 Axios responseType 为 json 时,不要传给传 XMLHttpRequest,以防止非 JSON 格局的响应内容失落
- Axios 依据响应头的 content-type 判断是否须要解析 JSON,以防止性能问题
通过申请拦截器实现不给 Axios 传递 transformResponse 配置,且将配置备份到其余字段上,而后在响应拦截器中将响应对象 response 传递给 transformResponse 解决。响应拦截器依据 response 提供的状态码、响应头和响应内容判断是否要进行 JSON 转换。
- 勾销 Axios validateStatus 的配置选项,默认所有大于 0 的状态码都是正确的状态码,而后在 Axios 拦截器 then 中进行数据解析(非 200 的可能也是 JSON,所以要复用 200 的 JSON 解析代码),并且依据异常情况抛出直观的谬误对象
- 内置默认解决表单类型的申请体
用法说明
eaxios 次要对响应的解决做了一些优化,除了以下局部,eaxios 的 api 与 axios 保持一致:
-
eaxios 申请配置的 transformResponse 传参和解决机会产生了变动
axios 在服务端响应内容后就会调用 transformResponse 进行响应转换,eaxios 响应后外部主动依据响应头和 responseType 进行 JSON 解析,而后将解析后的数据和 response 传给 transformResponse,transformResponse 返回的数据最终会被 Promise resovle 给内部调用者。
假如服务端返回的数据结构为
{code: 0, message: 'success', data: {} }
,code 为 0 示意正确的响应,非 0 示意异样,接口申请的代码示例如下所示:const eaxios = require('eaxios'); eaxios.defaults.transformResponse = [function (data, response) {if (typeof data === 'object') { // 默认约定有胜利解析 JSON 对象,就认为服务端胜利响应,且有提供错误码 if (data.code === 0) {return data.data;} else {throw eaxios.createError(data.message, data.code, response); } } else { // 50x 等服务异常情况 throw eaxios.createError( data, response.config.responseError.SERVER_ERROR, response ); } }, ]; return eaxios('https://run.mocky.io/v3/4f503449-0349-467e-a38a-c804956712b7') .then((data) => {console.log('success', data.id); }) .catch((error) => {console.log('failure', error.code); // UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和业务错误码 });
ps:如果存在服务单接口申请标准,能够通过 eaxios.create 创立实用于不同接口标准的申请函数。
-
eaxios 的申请处理函数 then 只会接管到 transformResponse 转换后的数据,对于网络、超时、服务端异样和业务异样等问题,会在 catch 接管一个 EaxiosError 类型的谬误对象。
interface EaxiosError<T = any> extends Error { config: EaxiosRequestConfig; code?: string; request?: any; response?: EaxiosResponse<T>; isAxiosError: boolean; toJSON: () => object;}
谬误处理函数能够依据错误码 code 来解决异样,code 可能的值为 UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和其余业务错误码。
ps:如果要定制错误码,能够在申请配置中增加配置项
responseError
。eaxios.defaults.responseError = {REQUEST_OFFLINE: '1'REQUEST_OFFLINE};
- eaxios 外部会主动序列化表单类型的申请参数,所以只有对象给 data 就行了。
代码示例
上面以 {code: 0, message: 'success', data: {} }
这样的接口标准为例,演示如何应用 eaxios。
const eaxios = require('eaxios');
const request = eaxios.create({
baseURL: 'https://run.mocky.io/v3',
timeout: 30000,
transformResponse: [function (data, response) {if (typeof data === 'object') {if (data.code === 0) {return data.data;} else {throw eaxios.createError(data.message, data.code, response);
}
} else {
throw eaxios.createError(
data,
response.config.responseError.SERVER_ERROR,
response,
);
}
},
],
});
request.interceptors.response.use(function (response) {return response;},
function (error) {if (error && error.code) {if (error.code === 'UNKNOWN') {console.log('未知谬误');
} else if (error.code === 'REQUEST_OFFLINE') {console.log('网络未连贯');
} else if (error.code === 'REQUEST_TIMEOUT') {console.log('网络有点慢,申请超时了');
} else if (error.code === 'SERVER_ERROR') {console.log('零碎出问题了');
} else if (error.code === 'RESPONSE_INVALID') {console.log('服务端 bug');
} else if (error.code === '10000') {
// 假如 10000 为登录会话过期
console.log('登录会话生效');
} else {console.log('依据状况是否要音讯提醒,还是内部解决')
}
}
throw error;
},
);
function printError(error) {
console.log(`code: ${error.code}, name: ${error.name}, message: ${error.message}, isAxiosError: ${error.isAxiosError}, stack:\n${error.stack}`,
);
}
function success() {console.log('>> success');
return request('/4f503449-0349-467e-a38a-c804956712b7')
.then((data) => {console.log('success', data);
})
.catch((error) => {printError(error);
});
}
function failure() {console.log('>> failure');
return request('/42d7c21d-5ae6-4b52-9c2d-4c3dd221eba4')
.then((data) => {console.log('success', data);
})
.catch((error) => {printError(error);
});
}
function invalid() {console.log('>> invalid');
return request('/1b23549f-c918-4362-9ac8-35bc275c09f0')
.then((data) => {console.log('success', data);
})
.catch((error) => {printError(error);
});
}
function server_500() {console.log('>> server_500');
return request('/2a9d8c00-9688-4d36-b2de-2dee5e81f5b3')
.then((data) => {console.log('success', data);
})
.catch((error) => {printError(error);
});
}
success().then(failure).then(invalid).then(server_500);
/* log
>> success
success {id: 1}
>> failure
登录会话生效
code: 10000, name: Error, message: error, isAxiosError: true, stack: ...
>> invalid
服务端 bug
code: RESPONSE_INVALID, name: SyntaxError, message: Unexpected token V in JSON at position 0, isAxiosError: true, stack: ...
>> server_500
零碎出问题了
code: SERVER_ERROR, name: Error, message: ..., stack: ...
*/
兼容性
eaxios 依赖 URLSearchParams 解决表单类型的申请参数,不反对的环境须要引入响应的 polyfill
-
core-js
require("core-js/modules/web.url-search-params.js")
- url-search-params-polyfill
相干链接
- Github:https://github.com/zhbhun/eaxios
- NPM:https://www.npmjs.com/package…