adapter 适配器
# getDefaultAdapter
axios 如何根据运行环境先选择默认的 adapter 呢?
/**
* If the browser has an XMLHttpRequest object, use the XHR adapter, otherwise use the HTTP
* adapter
*
* @returns {Function}
*/
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 如果支持 XMLHttpRequest,就是用 XHR。
- 不支持 XHR 则使用 nodeJS 的 HTTP 模块。
- 这两种方式分别用于 web 环境和 nodejs 环境。
# XHR Adapter
XHR Adapter 用于 web 环境。
function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
var responseType = config.responseType;
var onCanceled;
// 删除取消请求的订阅,cancelToken或者signal是两种取消请求的方式
function done() {
if (config.cancelToken) {
config.cancelToken.unsubscribe(onCanceled);
}
if (config.signal) {
config.signal.removeEventListener("abort", onCanceled);
}
}
if (utils.isFormData(requestData) && utils.isStandardBrowserEnv()) {
delete requestHeaders["Content-Type"]; // Let the browser set it
}
var request = new XMLHttpRequest();
// HTTP basic authentication
if (config.auth) {
var username = config.auth.username || "";
var password = config.auth.password
? unescape(encodeURIComponent(config.auth.password))
: "";
requestHeaders.Authorization = "Basic " + btoa(username + ":" + password);
}
var fullPath = buildFullPath(config.baseURL, config.url);
request.open(
config.method.toUpperCase(),
buildURL(fullPath, config.params, config.paramsSerializer),
true
);
// Set the request timeout in MS
request.timeout = config.timeout;
function onloadend() {
if (!request) {
return;
}
// Prepare the response
var responseHeaders =
"getAllResponseHeaders" in request
? parseHeaders(request.getAllResponseHeaders())
: null;
var responseData =
!responseType || responseType === "text" || responseType === "json"
? request.responseText
: request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request,
};
settle(
function _resolve(value) {
resolve(value);
done();
},
function _reject(err) {
reject(err);
done();
},
response
);
// Clean up request
request = null;
}
if ("onloadend" in request) {
// Use onloadend if available
request.onloadend = onloadend;
} else {
// Listen for ready state to emulate onloadend
request.onreadystatechange = function handleLoad() {
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;
}
// readystate handler is calling before onerror or ontimeout handlers,
// so we should call onloadend on the next 'tick'
setTimeout(onloadend);
};
}
// Handle browser request cancellation (as opposed to a manual cancellation)
request.onabort = function handleAbort() {
if (!request) {
return;
}
reject(
new AxiosError(
"Request aborted",
AxiosError.ECONNABORTED,
config,
request
)
);
// Clean up request
request = null;
};
// Handle low level network errors
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(
new AxiosError("Network Error", AxiosError.ERR_NETWORK, config, request)
);
// Clean up request
request = null;
};
// Handle timeout
request.ontimeout = function handleTimeout() {
var timeoutErrorMessage = config.timeout
? "timeout of " + config.timeout + "ms exceeded"
: "timeout exceeded";
var transitional = config.transitional || transitionalDefaults;
if (config.timeoutErrorMessage) {
timeoutErrorMessage = config.timeoutErrorMessage;
}
reject(
new AxiosError(
timeoutErrorMessage,
transitional.clarifyTimeoutError
? AxiosError.ETIMEDOUT
: AxiosError.ECONNABORTED,
config,
request
)
);
// Clean up request
request = null;
};
// Add xsrf header
// This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native.
if (utils.isStandardBrowserEnv()) {
// Add xsrf header
var xsrfValue =
(config.withCredentials || isURLSameOrigin(fullPath)) &&
config.xsrfCookieName
? cookies.read(config.xsrfCookieName)
: undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// Add headers to the request
if ("setRequestHeader" in request) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (
typeof requestData === "undefined" &&
key.toLowerCase() === "content-type"
) {
// Remove Content-Type if data is undefined
delete requestHeaders[key];
} else {
// Otherwise add header to the request
request.setRequestHeader(key, val);
}
});
}
// Add withCredentials to request if needed
if (!utils.isUndefined(config.withCredentials)) {
request.withCredentials = !!config.withCredentials;
}
// Add responseType to request if needed
if (responseType && responseType !== "json") {
request.responseType = config.responseType;
}
// Handle progress if needed
if (typeof config.onDownloadProgress === "function") {
request.addEventListener("progress", config.onDownloadProgress);
}
// Not all browsers support upload events
if (typeof config.onUploadProgress === "function" && request.upload) {
request.upload.addEventListener("progress", config.onUploadProgress);
}
if (config.cancelToken || config.signal) {
// Handle cancellation
// eslint-disable-next-line func-names
onCanceled = function (cancel) {
if (!request) {
return;
}
reject(
!cancel || cancel.type ? new CanceledError(null, config, req) : cancel
);
request.abort();
request = null;
};
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted
? onCanceled()
: config.signal.addEventListener("abort", onCanceled);
}
}
if (!requestData) {
requestData = null;
}
var protocol = parseProtocol(fullPath);
if (protocol && platform.protocols.indexOf(protocol) === -1) {
reject(
new AxiosError(
"Unsupported protocol " + protocol + ":",
AxiosError.ERR_BAD_REQUEST,
config
)
);
return;
}
// Send the request
request.send(requestData);
});
};
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
xhrAdapter 的处理过程如下:
- 创建
XMLHttpRequest
。 - 根据 auth 配置添加 Authorization header。
- 计算发送请求的 url。
request.open
配置请求。- 设置
request.timeout
。 - 注册
onloadend
事件,支持request.onloadend
则使用request.onloadend
,使用使用request.onreadystatechange
监听request.readyState
为 4。 - 注册
request.onabort
事件,抛出Request aborted
消息的AxiosError
。 - 注册
request.onerror
事件,抛出Network Error
消息的AxiosError
。 - 注册
request.ontimeout
事件,抛出timeout exceeded
消息的AxiosError
。 - 在 web 环境中,如果配置了 xsrfCookieName,则将 xsrfCookieName 设置在 header 上作为预防 xsrf 攻击的 token。
- 处理请求的 headers。
- 处理
request.withCredentials
、request.responseType
。 - 如果配置了
onDownloadProgress
,则在 request 上监听progress
事件。 - 如果配置了
cancelToken
或者signal
,前者在cancelToken
上订阅onCanceled
,后者监听 signal 的abort
事件,触发onCanceled
回调。onCanceled
中抛出CanceledError
错误,并且request.abort
。 - 检查是否是支持的协议
protocol
,否则抛出Unsupported protocol
错误的 AxiosError。 - 发送请求
request.send(requestData)
。 - 请求发出后,
onloadend
处理 response 对象。通过验证 status 即validateStatus
resolve response 或者 reject 一个Request failed
AxiosError。validateStatus 默认的校验规则是status >= 200 && status < 300
。 onloadend
之后调用done
,做 cleanup 的工作。
# HTTP Adapter
HTTP Adapter 用于 NodeJS 环境。
编辑 (opens new window)
上次更新: 2022/09/06, 14:25:16