前言
这应该是一个大多数都常用的请求库, 因为它可以支持多种配置, 跨平台实现, 返回 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();