Axios是近年来备受推崇的一个网络请求库,它以基于Promise的方式封装了浏览器的XMLHttpRequest和服务器端node http请求,使得我们可以用es6推荐的异步方式处理网络请求。
功能特性:
- 从浏览器创建XMLHttpRequest
- 从node.js创建http请求
- 支持Promise API
- 拦截请求与响应
- 转换请求与响应数据
- 取消请求
- 自动转换JSON数据
- 支持客户端XSRF攻击防护
axios.interceptors
axios.interceptors
拦截器主要用来作什么?和路由拦截有什么区别
- 一个是接口的request, response数据处理
- 一个是对业务处理,比如页面权限控制
axios拦截
axios拦截器分为请求拦截器和响应拦截器。用户可以通过then方法为请求添加回调,而拦截器中的回调将在then中的回调之前执行
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
移除已经设置的拦截器
var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
给自定义的axios实例添加拦截器
var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
import axios from 'axios'
import env from '@/env'
import Utils from '@/components/Utils'
import {
Message
} from 'element-ui'
// 获取系统ID
const systemId = Utils.getQueryString('systemId')
if (systemId) {
window.systemId = systemId
} else {
// 没有系统ID
location.href = env.dataCloud
}
const auth = (response) => {
// 未登录
if (response && ((response.status === 401) || (response.status === 412))) {
location.href = env.dataCloud
}
}
const instance = axios.create({
baseURL: `${env.api}${env.prefix.develop}`,
params: {
systemId
},
withCredentials: true
})
instance.nterceptors.response.use((response) => {
auth(response)
if (response && response.data && response.data.statusCode !== '0') {
Message.error((response.data && response.data.msg) || response.data.errmsg || '抱歉')
return Promise.reject(response).catch(() => { })
}
return Promise.resolve(response.data)
}, (error) => {
let msg = '网络错误,请稍后再试'
if (error && error.response && error.response.status === 401) {
msg = '请登录'
}
const notice = document.getElementsByClassName('ivu-message-error')
if (notice.length === 0) {
Message.error(msg)
}
auth(error && error.response)
return Promise.reject(error).catch(() => { })
})
export default instance
路由拦截
login.js
main.js
axios/lib/core/Axios.js
axios/lib/core/InterceptorManager.js
拦截器上有request,response。分别用于拦截发送,接收。
经典场景
比如这样一个场景:
在进行敏感操作(常见敏感操作如购买
,获取列表
等)之前,每个请求需要携带token,但是token 有有效期,token 失效后需要换取新的token并继续请求。
需求分析:
每个请求都需要携带 token ,所以我们可以使用 axios request 拦截器,在这里,我们给每个请求都加 token,这样就可以节省每个请求再一次次的复制粘贴代码。
token 失效问题,当我们token 失效,我们服务端会返回一个特定的错误表示,比如 token invalid,但是我们不能在每个请求之后去做刷新 token 的操作呀,所以这里我们就用 axios response 拦截器,我们统一处理所有请求成功之后响应过来的数据,然后对特殊数据进行处理,其他的正常分发。
功能实现
在 main.js 注册 axios
jsVue.use(Vuex)
Vue.use(VueAxios, axios)
Vue.use(qs)
注:qs,使用axios,必须得安装 qs,所有的Post 请求,我们都需要 qs,对参数进行序列化。
在 request 拦截器实现
axios.interceptors.request.use(
config => {
config.baseURL = '/api/'
config.withCredentials = true // 允许携带token ,这个是解决跨域产生的相关问题
config.timeout = 6000
let token = sessionStorage.getItem('access_token')
let csrf = store.getters.csrf
if (token) {
config.headers = {
'access-token': token,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
if (config.url === 'refresh') {
config.headers = {
'refresh-token': sessionStorage.getItem('refresh_token'),
'Content-Type': 'application/x-www-form-urlencoded'
}
}
return config
},
error => {
return Promise.reject(error)
}
)
//在 response 拦截器实现
axios.interceptors.response.use(
response => {
// 定时刷新access-token
if (!response.data.value && response.data.data.message === 'token invalid') {
// 刷新token
store.dispatch('refresh').then(response => {
sessionStorage.setItem('access_token', response.data)
}).catch(error => {
throw new Error('token刷新' + error)
})
}
return response
},
error => {
return Promise.reject(error)
}
)
源码分析
[libcoreInterceptorManager.js L5]()
'use strict';
var utils = require('./../utils');
function InterceptorManager() {
this.handlers = [];
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
// InterceptorManager的对象中是通过handlers数组变量存储拦截器,
// 数组每项同时包含了分别作为Promise中resolve和reject的回调。
// InterceptorManager类中还包含了对该数组变量的添加、 移除、 遍历方法。
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
// 移除方法是通过直接将该项设为null实现的,
// 而不是用splice剪切该数组, 遍历方法中也增加了相应的null值处理。
// 这样做一方面使得每一项ID保持为项的数组索引不变,
// 另一方面也避免了重新剪切拼接数组的性能损失。
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。