这应该是一个大多数都经常使用的请求库,由于它能够支持多种配置,跨平台实现,返回promise进行链式调用.彻底过一遍源码能够提高本身对请求库的理解知识node
axios源码系列(一) --- 目录结构和工具函数
axios源码系列(二) --- 适配器内部
axios源码系列(三) --- 默认配置和取消请求
axios源码系列(四) --- Axios和dispatchRequest与拦截器ios
这个Axios库的默认参数配置json
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; }
这里只有四件事:axios
Content-Type
为浏览器默认的编码格式adapters
里面解析头部名函数的源码以下segmentfault
过滤得出是否不一样大小写字母的等价请求头,是的话则设置新的请求头移除旧的请求头promise
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;
主要提供了几个默认配置cookie
配置 | 做用 |
---|---|
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'} |
/** * 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的构造函数,代表取消某个请求,提供一句取消信息和标记app
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
对象用于取消请求的操做:函数
执行传入的回调函数参数
reason
属性表明已经发出取消请求的操做,则中断操做Cancel
对象而且改变Promise状态返回该对象原型上绑定throwIfRequested
方法,若是实例已经取消能够直接抛出取消信息
构造函数上绑定source
方法,调用以后能够返回新的CancelToken
实例和cancel
方法
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();