基于ElementUI的axios请求拦截统一配置

前言

在开发过程中,不可避免的肯定会涉及到异步接口请求。而对于同一项目中,肯定会存在请求的某些异常,处理方式是一样的。如果在每个调用接口请求的地方去处理异常,那么整个项目中,尤其是大项目的时候,就会存在一大堆相同的处理,导致代码的冗余性、重复性特别明显。在未来想对同一种异常做出变更的时候,就需要修改每个调用请求接口的地方,这是非常不利于维护的。这就迫切需要我们对于请求拦截处理做出统一封装,既有利于代码的简洁性、可读性,更有利于代码的可维护性。以下给出统一处理拦截方案。

技术栈

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的版本了。在此版本,通过如上方式,可以正常地添加自定义配置项。
对于新版本是否支持自定义配置项及如支持,添加方法如何。如有了解的大咖,欢迎留言讨论、告知啊。哈哈哈!!

你可能感兴趣的:(axios,element-ui,vue,vue)