axios源码解析-dispatchRequest
dispatchRequest.js是整个axios中处理请求最核心的方法,他包括了验证取消请求,数据转化,Ajax封装(适配器的使用),我们来看一下这一部分的源码
// 检验是否需要取消请求
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
首先,axios定义了一个方法,通过检查config中的一个配置来决定是否取消请求。之后,这个js文件导出了dispatchRequest这个函数,这个函数首先调用了检验取消请求的
函数,然后,调用了transformData这个方法,在发送前对数据进行处理
module.exports = function dispatchRequest(config) {
// 检验是否需要取消请求
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
// 传入数据和请求头,并用transfromData进行修改
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 它只能用与 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
// 你可以修改请求头。
// transformRequest: [function (data, headers) {
// // 对发送的 data 进行任意转换处理
// return data;
// }],
// 这里的data是在发送请求前最终处理完成后返回的data
// 调用transfrom处理请求前的参数
config.data = transformData.call(
config,
config.data,
config.headers,
config.transformRequest //这一项作为回调函数
);
这个transfromData的函数究竟做就什么事呢,我们来看看transfromData这个函数究竟做了什么事
// 解析并在请求前处理数据
module.exports = function transformData(data, headers, fns) {
//这里的fns是一个函数数组,在axios自定义的foreach中,如果参数是数组,会让每一项都执行回调函数
var context = this || defaults;
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
// 这里最终相当于this被绑定在了context上,将data和headers作为参数执行transfrom中的每一个方法,并返回一个data
data = fn.call(context, data, headers);
});
return data;
};
这个函数有一点复杂,各种参数相互传递,看的人有点懵,其实理清各种参数的关系还是很好理解的,这里跟拦截器那部分的处理也很相似,将所有要进行处理的函数放到一个数组里,然后用foreach(这里的foreach是自定义的,跟JS里的foreach相似但不完全一样)进行遍历,对这个函数数组里的每一项绑定this并执行,更新data,最终,这个部分就会执行我们在配置里定义的所有transfromData方法并更新数据data
之后,对请求头进行了一些处理,合并了一些配置,并除去了一些无用属性
// 处理优先级,用后进来的对象去修改和补充先进来的对象的属性参数
// 抽出请求头的common和当前请求的methods合并到首层结构
// 合并修改数据后的headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
// 清除配置头中的无用参数
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
然后就是使用适配器的部分,首先判断有没有自定义适配器,没有的话直接使用默认配置中的适配器
var adapter = config.adapter || defaults.adapter;
我们来看一下defaults.js这部分中对适配器的定义
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
}
return adapter;
}
adapter: getDefaultAdapter(),
我们可以看到,适配器其实是一个函数,他会判断当前运行环境,如果是浏览器环境,就执行对AJAX的封装部分的函数,如果是node环境,就执行对HTTP进行封装的函数,不论是那个,都一定是一个异步函数,因此,下一步中,直接传入配置并调用then方法,执行请求部分的回调函数,确认是否取消请求,并再次transfromData对返回的响应数据进行处理,这部分跟请求前的数据处理原理是意义的。如果失败的话,同样对能够返回的数据进行处理。这个部分返回的是一个异步函数,用于在执行链的阶段跟拦截器执行相同的链式调用
return adapter(config).then(function onAdapterResolution(response) {
// 适配器的成功回调
throwIfCancellationRequested(config);
// 处理并返回信息
// Transform response data
response.data = transformData.call(
config,
response.data,
response.headers,
// 对响应的数据进行统一处理
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
// 适配器的失败回调
// 如果没有取消请求,继续对数据包数据进行处理
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
// 请求失败也会对响应的data和响应头进行处理
reason.response.data = transformData.call(
config,
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);