在上一节的入口文件中,我们都知道axios是基于默认配置创建的,那么这个默认配置到底配置了哪些东西呢?
这一节,我们就来看一看lib/defaults.js的源码内容。
辅助函数
正如其名字一样,normalizeHeaderName方法用来标准化请求头,看一下其代码实现:
1 2 3 4 5 6 7 8
| function normalizeHeaderName(headers, normalizedName) { utils.forEach(headers, function processHeader(value, name) { if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { headers[normalizedName] = value; delete headers[name]; } }); };
|
方法接收两个参数,请求头headers和标准请求头名称normalizedName,所以我们每次只会对一个请求头参数名称进行标准化。
utils.forEach我们在上一节说到过,在这里的作用就是循环请求头,然后把每个请求头的值和名字分别传入处理函数。
处理函数内部的操作很简单,如果请求头的名称与标准名称不同但是两者转为大写后相同,那么这个请求头的名称就是不标准的,我们用标准化的请求头名称进行替换。
比如我们有一个请求头:
1 2 3
| headers = { 'CONTENT-TYPE': 'application/json' }
|
我们调用normalizedHeaderName方法并传入标准名称Content-Type
1
| normalizedHeaderName(headers, 'Content-Type')
|
经过处理后我们得到的结果是这样的:
1 2 3
| headers = { 'Content-Type': 'application/json' }
|
getDefaultAdapter
见名知意,这个方法是用来获取默认适配器的。
1 2 3 4 5 6 7 8 9
| function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== 'undefined') { adapter = require('./adapters/xhr'); } else if (typeof process !== 'undefined') { adapter = require('./adapters/http'); } return adapter; }
|
前边我们说过,axios分别为浏览器环境和NodeJs环境做了不同的适配器,所以在这个方法中进行了判断返回正确的适配器。
setContentTypeIfUnset
一样的见名知意,如果没有设置ContentType那么就给设置ContentType
这里用到一个辅助方法isUndefined,方法内容非常简单,就是判断参数是否为undefined
1 2 3
| function isUndefined(val) { return typeof val === 'undefined'; }
|
然后看一下setContentTypeIfUnset的源码内容,很简单,不多解释:
1 2 3 4 5 6
| function setContentTypeIfUnset(headers, value) { if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; } }
|
这里并不是直接配置请求头,而是做了一些处理,axios对默认配置中的headers做了分类,比如通用的配置、适用于某个方法的配置等,然后在dispatchRequest中根据具体的请求方法去取出对应的配置内容来使用。
接收类型限制Accept是一个通用配置,意味着在所有请求方法都是可用的:
1 2 3 4 5
| defaults.headers = { common: { 'Accept': 'application/json, text/plain, */*' } };
|
对于get,delete,head请求,由于不需要携带body,只声明了一个空对象
1 2 3
| utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { defaults.headers[method] = {}; });
|
对于post,put,patch请求,声明了Content-Type。
源码中有Content-Type的默认声明
1 2 3 4 5 6 7
| var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' };
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); });
|
这里还用到了辅助方法merge,也不复杂,就是做了一个合并操作,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function merge() { var result = {}; function assignValue(val, key) { if (typeof result[key] === 'object' && typeof val === 'object') { result[key] = merge(result[key], val); } else { result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { forEach(arguments[i], assignValue); } return result; }
|
注意,如果需要合并的两个对象中有相同的属性,但是对应的值不全是对象的话,后传入的会把前边的覆盖掉。
这样我们对于headers的内容基本也看完了,现在的defaults应该是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| let defaults = { headers: { common: { 'Accept': 'application/json, text/plain, */*' }, get: {}, delete: {}, head: {}, post: { 'Content-Type': 'application/x-www-form-urlencoded' }, put: { 'Content-Type': 'application/x-www-form-urlencoded' }, patch: { 'Content-Type': 'application/x-www-form-urlencoded' }, } }
|
defaults其他内容
其他内容就比较简单了,直接贴出来看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| var defaults = { adapter: getDefaultAdapter(), transformRequest: [function transformRequest(data, headers) { 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) { if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { } } return data; }],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1, validateStatus: function validateStatus(status) { return status >= 200 && status < 300; } };
|
transformRequest和transformResponse后边还会看到,在这里就先简单说一下吧,到后边还会见到他们的。他们两个的用法其实是一样的,只是一个处理请求数据,一个处理响应数据。他们都是数组,数组元素是函数,每个函数接收两个参数data和headers。
在defaults中设置了对与请求数据和响应数据的默认转换方式,他们最后会在dispatchRequest中执行,执行时会将数据、请求头、transformRequest或者transformResponse数组传入transformData方法,在这个方法中会依次执行数组中的方法,并将数据和请求头传入。
需要注意的是不管是transformRequest还是transformResponse,对数据转换后需要将数据返回出来
我们现在已经大概知道这两个数组中的方法是做什么的了,具体在哪里进行调用咱们先不关注,会在后续内容中讲解,先来看一下defaults提供的默认数据转换内容:
请求内容的处理并不复杂,首先是对与Content-Type请求头进行了标准化,前边说到的方法终于在这里用到了。
其次用到了一些辅助方法去判断数据的格式,根据不同数据格式对其进行不同的转换,如果没有配置Content-Type,那么有些格式还需要设置对应的Content-Type。
比如我们最常见的情况,我们传递的数据是一个对象,如果我们没有配置Content-Type,这里就给我们请求头的Content-Type设置为application/json;charset=utf-8,然后将我们的对象转换为JSON字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function transformRequest(data, headers) { 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; }
|
对于响应内容的默认处理比较简单哈,如果响应内容是字符串,就考虑是不是JSON字符串,尝试将其转换为对象形式。
1 2 3 4 5 6 7 8
| function transformResponse(data) { if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { } } return data; }
|
完整源码
贴一下完整的defaults源码内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| 'use strict';
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') { adapter = require('./adapters/xhr'); } else if (typeof process !== 'undefined') { adapter = require('./adapters/http'); } return adapter; }
var defaults = { adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) { 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) { if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { } } return data; }], 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;
|