adapter 做了一件事非常简单,就是根据不同的环境 使用不同的请求。
如果用户自定义了adapter,就用config.adapter。
否则就是默认是default.adpter.
adpter 适配浏览器的xhr,和node的http
import utils from './utils'
var defaults = {
// ...
// 请求超时时间,默认不超时
timeout: 0,
// 转换请求
transformRequest: [function transformRequest(data, headers) {
// 判断 data
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();
}
// 如果 data 是对象,或 Content-Type 设置为 application/json
if (utils.isObject(data) || (headers && headers['Content-Type'] === 'application/json')) {
// 设置 Content-Type 为 application/json
setContentTypeIfUnset(headers, 'application/json');
// 将 data 转换为 json 字符串返回
return JSON.stringify(data);
}
return data;
}],
// 转换响应数据
transformResponse: [function transformResponse(data) {
// ...
// if (strictJSONParsing || (forcedJSONParsing && utils.isString(data) && data.length)) {
if (typeof data === 'string') {
try {
// 将 data 转换为 json 对象并返回
return JSON.parse(data);
} catch (e) {
// ...
}
}
return data;
}],
// 判断响应状态码的合法性: [200, 299]
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
},
adapter: getDefaultAdapter()
}
function getDefaultAdapter () {
var adapter;
// 判断当前环境
if (typeof XMLHttpRequest !== 'undefined') {
// 浏览器
adapter = require('./adapters/xhr')
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// NODE环境,使用http模块
adapter = require('./adapters/http')
}
return adapter
}
基于promise 的xhr请求
module.exports = function xhrAdapter (config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
// 设置完整路径,拼接url,例如:https://www.baidu,com + /api/test
var fullPath = buildFullPath(config.baseURL, config.url);
// 初始化一个请求,拼接url,例如:https://www.baidu,com/api/test + ?a=10&b=20
xhr.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
// 超时断开,默认 0 永不超时
xhr.timeout = config.timeout;
xhr.onreadystatechange = function() {
// request不存在或请求状态不是4, 直接结束
if (!request || request.readyState !== 4) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// 准备response对象
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
}
// 根据响应状态码来确定请求的promise的结果状态(成功/失败)
settle(resolve, reject, response);
// 将请求对象赋空
request = null;
};
// 超时触发该事件
xhr.ontimeout = function handleTimeout() {
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config,'ECONNABORTED',request));
// Clean up request
request = null;
};;
// 取消请求触发该事件
xhr.onabort = function handleAbort() {
if (!request) {
return;
}
// reject promise, 指定aborted的error
reject(createError('Request aborted', config, 'ECONNABORTED', request));
// Clean up request
request = null;
};
// 一般是网络问题触发该事件
xhr.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
// Clean up request
request = null;
};
;
// 标准浏览器(有 window 和 document 对象)
if (utils.isStandardBrowserEnv()) {
// 非同源请求,需要设置 withCredentials = true,才会带上 cookie
var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// request对象携带 headers 去请求
if ('setRequestHeader' in xhr) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
// data 为 undefined 时,移除 content-type,即不是 post/put/patch 等请求
delete requestHeaders[key];
} else {
xhr.setRequestHeader(key, val);
}
});
}
// ...,取消请求的
// 发送请求
xhr.send(requestData)
})
}
'use strict';
var createError = require('./createError');
/**
* 根据响应状态码来确定是成功还是失败
*
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
module.exports = function settle(resolve, reject, response) {
// 判断响应状态码的合法性
var validateStatus = response.config.validateStatus;
if (!validateStatus || validateStatus(response.status)) {
resolve(response);
} else {
reject(createError(
'Request failed with status code ' + response.status,
response.config,
null,
response.request,
response
));
}
};
'use strict';
var enhanceError = require('./enhanceError');
/**
* 创建error对象的函数
*
* Create an Error with the specified message, config, error code, request and response.
*
* @param {string} message The error message.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [request] The request.
* @param {Object} [response] The response.
* @returns {Error} The created error.
*/
module.exports = function createError(message, config, code, request, response) {
var error = new Error(message);
return enhanceError(error, config, code, request, response);
};
'use strict';
var utils = require('./../utils');
// 替换
function encode(val) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, '+').
replace(/%5B/gi, '[').
replace(/%5D/gi, ']');
}
/**
* 构建一个带参数的url
*
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
module.exports = function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url;
}
var serializedParams;
if (paramsSerializer) {
serializedParams = paramsSerializer(params);
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString();
} else {
var parts = [];
utils.forEach(params, function serialize(val, key) {
if (val === null || typeof val === 'undefined') {
return;
}
if (utils.isArray(val)) {
key = key + '[]';
} else {
val = [val];
}
utils.forEach(val, function parseValue(v) {
if (utils.isDate(v)) {
v = v.toISOString();
} else if (utils.isObject(v)) {
v = JSON.stringify(v);
}
parts.push(encode(key) + '=' + encode(v));
});
});
serializedParams = parts.join('&');
}
if (serializedParams) {
var hashmarkIndex = url.indexOf('#');
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex);
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
}
return url;
};
通过上面对 adapter 的分析,可以发现如果自定义 adapter 的话,是可以接管 axios 的请求和响应数据的,因此可以自定义 adapter 实现 mock;
const mockUrl = {
'/mock': {data: xxx}
};
const instance = Axios.create({
adapter: (config) => {
if (!mockUrl[config.url]) {
// 调用默认的适配器处理需要删除自定义适配器,否则会死循环
delete config.adapter
return Axios(config)
}
return new Promise((resolve, reject) => {
resolve({
data: mockUrl[config.url],
status: 200,
})
})
}
})