Eaxios 是基于 axios 封装的网络请求库,在保持 API 与 axios 基本一致的状况下,简化服务端响应内容和各类异常状况的处理。前端
如上图所示,是一次 Ajax 请求可能输出的结果,在前端咱们须要根据输出结果给用户不一样的提示。ios
可是,现有的 Axios 库对于异常结果没有提供较好的封装,Axios Promise catch 里包含各类类型的错误,并且没有提供错误码来识别请求失败的缘由。并且不少服务端接口会返回本身的错误码,这样在 Axios Promise then 里也须要处理业务异常。git
此外,Axios 自己以下所述的一些问题和局限性。github
对于 500 等错误,响应内容会丢失,因此不要去配置 responseType 为 json,对于使用者来讲容易采到这个坑。web
ps:虽然 Axios 官方文档声明 responseType 是 json,实际上底层调用 XMLHttpRequest 的 responseType 是没有传值的,应该是为了规避这个问题。npm
ps:应该是为了规避上一个问题,默认提供了一个响应处理函数进行 JSON 解析,可是这会影响性能(500 等响应内容值较多时,会形成页面卡顿)。虽然 transformResponse 能够转换 response,实际接收到的参数是 response.data,因此没法判断具体状况来决定是否进行解析 JSON。json
理想状况下,使用者但愿 then 返回有效的数据,catch 返回各类错误状况:请求被取消、网络异常、网络超时、服务端异常、服务端数据格式错误、业务异常。axios
content-type
为 application/x-www-form-urlencoded
类型的请求体,使用起来不够方便优化方案:api
经过请求拦截器实现不给 Axios 传递 transformResponse 配置,且将配置备份到其余字段上,而后在响应拦截器中将响应对象 response 传递给 transformResponse 处理。响应拦截器根据 response 提供的状态码、响应头和响应内容判断是否要进行 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 };
下面以 { 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
require("core-js/modules/web.url-search-params.js")