前言
在开发过程中,不可避免的肯定会涉及到异步接口请求。而对于同一项目中,肯定会存在请求的某些异常,处理方式是一样的。如果在每个调用接口请求的地方去处理异常,那么整个项目中,尤其是大项目的时候,就会存在一大堆相同的处理,导致代码的冗余性、重复性特别明显。在未来想对同一种异常做出变更的时候,就需要修改每个调用请求接口的地方,这是非常不利于维护的。这就迫切需要我们对于请求拦截处理做出统一封装,既有利于代码的简洁性、可读性,更有利于代码的可维护性。以下给出统一处理拦截方案。
技术栈
1:element UI
2:axios
3:underscore
具体方案
结合axios官网,详细方案如下
废话不多说,上代码
import axios from 'axios'
import {
Message,
MessageBox,
Loading
} from 'element-ui'
import _ from 'underscore'
// 防重复提交的安全机制,请求出错后会获取新的nonce数据,一般用于post请求,当作参数一并(或通过header)送给后端去验证
// import nonce from './nonce'
/*
* 配置说明:
* method:请求方式,默认get
* url:请求url
* nonce:是否开启nonce,默认关闭
* showError:是否显示通用错误提示
* loading:是否显示loading
*/
// nonce(); //触发获取nonce token数据的异步请求
// 请求成功返回码(与后端约定)
const RET_CODE_OK = '000000'
// 创建axios实例,设置前端请求超时时间 (0 表示无超时时间),如果请求花费超过 `timeout` 的时间,请求将被中断
const instance = axios.create({ timeout: 1000 * 10})
axios.defaults.baseURL = process.env.BASE_URL // process.env.BASE_URL是自己配的全局环境变量,按需求写
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;utf-8'
const pending = {}
const CancelToken = axios.CancelToken
let loading = null
const _getRequestIdentify = (config) => {
let url = config.url
return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data))
}
/*
* @param item Object data 对象
* @key 不需传,递归用
* @return {String}
*/
const _params = (item, key) => {
if(_.isArray(item)) {
return _.map(item, (value, index) => _params(value, key + '[' + index + ']')).join('&')
}
if(_.isObject(item)) {
return _.map(item, (value, itemKey) => _params(value, (key ? key + '.' : '') + itemKey)).join('&')
}
if(item === null || item === undefined) {
return key + '='
}
return key + '=' encodeURIComponent(item)
}
// 请求拦截器
instance.interceptors.request.use( config => {
// 拦截重复请求(当前正在进行的相同的请求)
let key = _getRequestIdentify(config)
if(pending[key]) {
pending[key]('取消重复请求')
delete pending[key]
}
config.cancelToken = new CancelToken (c => {
pending[key] = c
});
/* if(config.method === 'post' && config.none && window.__NONCE ) {
config.headers['X-NONCE-TOKEN'] = window.__NONCE
} */
if(config.data) {
config.data = _params(config.data)
}
if(config.loading) {
loading = Loading.service({
lock: false,
text: '加载中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'loading'
})
}
return config
}, error => {
// 请求错误处理
return Promise.reject(error)
});
// 响应拦截器
instance.interceptors.response.use(response => {
let {config} = response
let key = _getRequestIdentify(config)
delete pending[key]
if(config.loading) {
setTimeout( _ => {
loading && loading.close()
}, 300)
}
let {data, retCode, retMsg } = response.data
if(retCode === RET_CODE_OK ){
return Promise.resolve(data || {})
} else {
const error = { retCode, retMsg}
if(config.showError) {
Message.error(retMsg || '系统繁忙,请稍后重试...')
}
// nonce()
return Promise.reject(error)
}
}, error => {
// 对响应错误做点什么
if(!error.response) {
loading && loading.close()
Message.error('系统繁忙,请稍后重试...')
Promise.reject(error)
return
}
let {config, status, data} = error.response
if(config.loading) {
setTimeout( _ => {
loading && loading.close()
}, 300)
}
// nonce()
switch (status) {
case 403:
MessageBox.alert('您的操作已超时!', () => {
window.location.reload()
})
default:
Message.error(data && data.message || '系统繁忙,请稍后重试...')
}
return Promise.reject(error);
});
export default (options) => {
var _params = {
method: !options.type ? 'get' : options.type.toLowerCase()
url: options.url,
// nonce: !!options.nonce
showError: options.showError !== false,
loading: options.loading !== false
}
if(_params .method === 'get') {
_params.params = options.data || {}
if(options.cache === false) {
Object.assign(_params.params, {_: Date.now()})
}
} else {
_params.data = options.data || {}
}
Object.assign( options, _params.params);
return instance.request(_params)
}
值得关注的是,axios高版本的似乎不支持添加自定义的配置项。在个人开发过程中,开始是直接用最新版本,后来有需要添加一些自定义配置项来配置时(比如前面的showError配置项)。当时查了一些资料去调整修改,发现并没有生效。后来项目工期原因也没有继续深入研究,只是直接把axios版本回退到0.15.3的版本了。在此版本,通过如上方式,可以正常地添加自定义配置项。
对于新版本是否支持自定义配置项及如支持,添加方法如何。如有了解的大咖,欢迎留言讨论、告知啊。哈哈哈!!