前言
这应该是一个大多数都常用的请求库,因为它可以支持多种配置,跨平台实现,返回promise进行链式调用.完全过一遍源码可以提升自己对请求库的理解知识
axios源码系列(一) — 目录结构和工具函数
axios源码系列(二) — 适配器内部
axios源码系列(三) — 默认配置和取消请求
axios源码系列(四) — Axios和dispatchRequest与拦截器
默认配置
axios/lib/defaults.js
这个Axios库的默认参数配置
var utils = require('./utils');
var normalizeHeaderName = require('./helpers/normalizeHeaderName');
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
};
function setContentTypeIfUnset(headers, value) {
if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
headers['Content-Type'] = value;
}
}
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;
}
这里只有四件事:
- 引入了工具库和解析头部名函数
- 设置默认
Content-Type
为浏览器默认的编码格式 - 定义设置headers内值为空的Content-Type对应值的方法
- 根据宿主环境获取对应的
adapters
里面解析头部名函数的源码如下
axios/lib/helpers/normalizeHeaderName.js
过滤得出是否不同大小写字母的等价请求头,是的话则设置新的请求头移除旧的请求头
var utils = require('../utils');
module.exports = function normalizeHeaderName(headers, normalizedName) {
utils.forEach(headers, function processHeader(value, name) {
if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
headers[normalizedName] = value;
delete headers[name];
}
});
};
这是暴露出去的对象
var defaults = {
adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
/**
* A timeout in milliseconds to abort a request. If set to 0 (default) a
* timeout is not created.
*/
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
}
};
defaults.headers = {
common: {
'Accept': 'application/json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
module.exports = defaults;
主要提供了几个默认配置
配置 | 作用 |
---|---|
adapter | 宿主环境对应的适配器 |
transformRequest | 内置转换函数,根据对应格式设置请求 |
transformResponse | 内置转换函数,如果是字符型则反序列化 |
timeout | 超时时间 |
xsrfCookieName | 用作 xsrf token 的值的cookie的名称 |
xsrfHeaderName | 承载 xsrf token 的值的 HTTP 头的名称 |
maxContentLength | 定义允许的响应内容的最大尺寸 |
validateStatus | 内置对于给定的HTTP 响应状态码是 resolve 或 reject promise的函数 |
headers | 默认请求头 |
[‘delete’, ‘get’, ‘head’] | 定义相关请求方式的默认请求头{} |
[post’, ‘put’, ‘patch’] | 定义相关请求方式的默认请求头,{‘Content-Type’: ‘application/x-www-form-urlencoded’} |
取消请求
axios/lib/cancel/Cancel.js
/**
* A `Cancel` is an object that is thrown when an operation is canceled.
*
* @class
* @param {string=} message The message.
*/
function Cancel(message) {
this.message = message;
}
Cancel.prototype.toString = function toString() {
return 'Cancel' + (this.message ? ': ' + this.message : '');
};
Cancel.prototype.__CANCEL__ = true;
module.exports = Cancel;
创建Cancel的构造函数,表明取消某个请求,提供一句取消信息和标记
axios/lib/cancel/CancelToken.js
var Cancel = require('./Cancel');
/**
* A `CancelToken` is an object that can be used to request cancellation of an operation.
*
* @class
* @param {Function} executor The executor function.
*/
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
/**
* Throws a `Cancel` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
module.exports = CancelToken;
创建CancelToken
对象用于取消请求的操作:
- 定义新的Promise并将改变状态的触发时机传递到外部
执行传入的回调函数参数
- 如果已经含有
reason
属性代表已经发出取消请求的操作,则中断操作 - 否则创建新的
Cancel
对象并且改变Promise状态返回该对象
- 如果已经含有
原型上绑定throwIfRequested
方法,如果实例已经取消可以直接抛出取消信息
构造函数上绑定source
方法,调用之后可以返回新的CancelToken
实例和cancel
方法
axios/lib/cancel/isCancel.js
module.exports = function isCancel(value) {
return !!(value && value.__CANCEL__);
};
通过是否拥有__CANCEL__
属性而得知某次请求是否已经取消了.
示例
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
或者
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// cancel the request
cancel();