Axios
简介
Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和Node.js. 拥有以下特性:
- 支持浏览器发送AJAX请求
- 支持Node.js发送 HTTP 请求
- 支持Promise
- 拦截请求和响应
- 转换请求和响应的数据
- 取消请求
- 自动转换JSON数据
- 客户端支持XSRF攻击
拦截请求和响应简介
前端向后端发送请求时,有很多配置参数以及请求返回的处理过程是相同的。假如在每一个HTTP请求中都配置相同的参数和加入请求处理函数,代码就会比较的ugly,而且不易维护。
想象一下下面的场景
- 应用基于JWT进行身份验证,需要在每个HTTP请求的
Header上添加token。 - 对于返回的错误信息,做统一的错误处理
针对上面的问题场景,Axios为我们提供了一种解决方案,拦截器。
使用拦截器
// Add a request interceptor
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);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
实现原理
问题:
- 拦截器处理函数什么时候触发?
axios实例对象
通过axios.interceptors.request.use()调用拦截器,得知拦截器是axios实例的属性。
通过下面的代码,Axios 构造函数上有属性interceptors, interceptors的request和response属性都是InterceptorManager类的实例对象
// lib/core/Axios.js
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
拦截处理函数收集
InterceptorManager类用于生成拦截器实例。
use方法主要是用于将promise成功状态和失败状态的处理函数添加到handlers
// lib/core/InterceptorManager.js
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.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
拦截请求和响应触发
在调用axios的请求方法时,会触发拦截器
axios支持的request method如下, 这些方法最后都会调用axios.request.axios.request触发请求和响应拦截器和向服务器端发送HTTP请求。
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.options(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
// lib/core/Axios.js
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: (config || {}).data
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: data
}));
};
});
axios.request
dispatchRequest 用于发送HTTP请求,请求拦截器处理函数放在该方法之前,响应拦截器处理函数放在该方法之后。
// lib/core/Axios.js
/**
* Dispatch a request
*
* @param {Object} config The config specific for this request (merged with this.defaults)
*/
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
// Set config.method
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// Hook up interceptors middleware
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};