本文参加了每周一起学习200行源码共读活动
上面文章中我们了解到,axios
使用 dispatchRequest
发送请求,dispatchRequest
位于 lib/core/dispatchRequest.js
dispatchRequest
的调用关系可以看下图。
dispatchRequest
下有两个方法:throwIfCancellationRequested
和 dispatchRequest
。
throwIfCancellationRequested
/**
* Throws a `CanceledError` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
if (config.signal && config.signal.aborted) {
throw new CanceledError();
}
}
这个方法主要是用来判断当次请求是否已经取消,如已取消,则抛出异常。
如果请求配置中存在 cancelToken
或 signal
,则表明当次请求已取消。
使用 cancelToken
取消时,抛出的异常是以传入 new CancelToken
的 executor
函数的 message
参数作为异常信息来抛出 CanceledError
异常。
使用 AbortController
,即 fetch API
的方式取消请求取消时,是直接抛出 CanceledError
异常。
dispatchRequest
这个方法是请求派发流程,主要分为三部分:请求头及 data
设置、根据当前环境或配置获取请求适配器、请求后响应处理。
请求头设置
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
config.data = transformData.call(
config,
config.data,
config.headers,
config.transformRequest
);
// Flatten 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];
}
);
...
};
第一部分主要是处理请求头及 data
,比如将请求头拍扁合并、删除掉一些请求头等。
这里面有一个 transformData
方法,该方法位于 /lib/core/transformData.js
,主要是调用 config.transformRequest
中传入的方法在请求前对请求中的 data
以及 headers
进行处理。
根据当前环境或配置获取请求适配器
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
...
var adapter = config.adapter || defaults.adapter;
...
};
第二部分是获取当前环境能够使用的请求适配器。此适配器可以通过 config
配置进行传入,也可以使用 axios
默认的 defaults.adapter
来获取请求。
如果使用传入的 adapter
, 则需要确保能够解析 axios
的配置信息。
如果使用默认的设置,则会判断当前环境是什么环境。 defaults.adapter
实质是一个环境判断加载函数,位于 lib/defaults/index.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;
}
请求后响应处理
请求作为一个 promise
执行,分别对请求后的正常及异常进行处理。
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
...
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) {
reason.response.data = transformData.call(
config,
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
正常和异常结果的处理流程都相差无几,主要是判断是否是取消请求了再使用 transformData
通过调用 config.transformResponse
来处理响应,后再返回响应。
transformData
/**
* Transform the data for a request or a response
*
* @param {Object|String} data The data to be transformed
* @param {Array} headers The headers for the request or response
* @param {Array|Function} fns A single function or Array of functions
* @returns {*} The resulting transformed data
*/
module.exports = function transformData(data, headers, fns) {
var context = this || defaults;
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
data = fn.call(context, data, headers);
});
return data;
};
transformData
位于 /lib/core/transformData.js
。这是一个请求/响应预处理的方法。
这个方法有三个参数, data
是最终结果集,headers
是请求/响应头,fns
则是 config.transformResponse
和 config.transformResponse
的传入参数,类型为函数数组。
这个方法调用一般是使用 call()
进行调用,在第一个参数中传入 config
,则 this
就变成了传入的 config
。
大致步骤就是将传入配置或默认配置作为上下文,调用通过 config.transformResponse
或 config.transformResponse
的传入参数函数对上下文、请求/响应体、请求/响应头进行处理,并将处理后的结果作为新的请求/响应体返回。
需要注意的是,调用回调函数使用的也是 call
,所以 context
上下文是作为函数中的 this
出现的。