壹:axios默认配置

在上一节的入口文件中,我们都知道axios是基于默认配置创建的,那么这个默认配置到底配置了哪些东西呢?

这一节,我们就来看一看lib/defaults.js的源码内容。

辅助函数

normalizeHeaderName

正如其名字一样,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) {
// 如果headers是undefined或者headers['Content-Type']是undefined,就把headers['Content-Type']设置为传入的value
if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
headers['Content-Type'] = value;
}
}

headers默认配置

这里并不是直接配置请求头,而是做了一些处理,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) {
// 如果result中有且是个对象,且新传入的也是个对象,需要继续合并
// 如果其中一个或者都不是对象的话后边的会覆盖前边的。
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = merge(result[key], val);
} else {
// 如果result中没有这个属性就放进去
result[key] = val;
}
}
// 对所有参数合并到result中
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) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],

// timeout:0就是没设置超时时间
timeout: 0,

xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',

maxContentLength: -1,
// 用来判断状态码是不是处于200-299
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
}
};

transformRequest和transformResponse

transformRequesttransformResponse后边还会看到,在这里就先简单说一下吧,到后边还会见到他们的。他们两个的用法其实是一样的,只是一个处理请求数据,一个处理响应数据。他们都是数组,数组元素是函数,每个函数接收两个参数dataheaders

defaults中设置了对与请求数据和响应数据的默认转换方式,他们最后会在dispatchRequest中执行,执行时会将数据、请求头、transformRequest或者transformResponse数组传入transformData方法,在这个方法中会依次执行数组中的方法,并将数据和请求头传入。

需要注意的是不管是transformRequest还是transformResponse,对数据转换后需要将数据返回出来

我们现在已经大概知道这两个数组中的方法是做什么的了,具体在哪里进行调用咱们先不关注,会在后续内容中讲解,先来看一下defaults提供的默认数据转换内容:

transformRequest

请求内容的处理并不复杂,首先是对与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;
}

transformResponse

对于响应内容的默认处理比较简单哈,如果响应内容是字符串,就考虑是不是JSON字符串,尝试将其转换为对象形式。

1
2
3
4
5
6
7
8
function transformResponse(data) {
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
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') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined') {
// For node use HTTP adapter
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) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
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;
作者

胡兆磊

发布于

2022-10-10

更新于

2022-10-23

许可协议