🌒 获取响应数据
# 需求分析
前面的实现中,发送的请求都可以从网络层面接收到服务器端返回的数据;但是代码层面并没有对响应数据进行任何的处理。
需要支持对响应数据的处理,并且支持 Promise
的链式调用的方式,如下:
axios({
method: 'post',
url: '/base/post',
headers: {
'content-type': 'application/json'
},
data: {
a: 1,
b: 2
}
}).then((res) => {
console.log(res)
})
2
3
4
5
6
7
8
9
10
11
12
13
可以获取到
res
对象,并且res
对象中包括服务器返回的数据data
,HTTP 响应状态码status
, 响应头headers
、请求配置对象config
以及请求的XMLHttpRequest
对象实例request
。
# 定义接口类型 AxiosReponse
如下:
export interface AxiosResponse {
data: any
status: number
statusText: string
headers: any
config: AxiosRequestConfig
request: any
}
2
3
4
5
6
7
8
另外 axios
函数返回的是一个 Promise
对象,可以定义一个 AxiosPromise
接口,继承于 Promise<AxiosResponse>
这个泛型接口:
export interface AxiosPromise extends Promise<AxiosResponse> {
}
2
3
此时,当
axios
返回的是AxiosPromise
类型,那么resolve
函数中的参数是一个AxiosResponse
类型。
对于 AJAX 请求的 response
,可以指定它的数据类型,通过设置 XMLHttpRequest
对象的 responseType
属性,可以给 axiosRequestConfig
类型添加一个可选属性:
export interface AxiosRequestConfig {
// ...
responseType?: XMLHttpRequestResponseType
}
2
3
4
responseType
的类型是一个XMLHttpRequestResponseType
类型,它的定义是"" | "arraybuffer" | "blob" | "document" | "json" | "text"
字符串字面量类型。
# 实现获取相应数据的逻辑
在 src/xhr.ts
中,添加 onreadystatechange
事件处理函数,并且让 xhr
函数返回的是 AxiosPromise
类型:
import { AxiosPromise, AxiosRequestConfig, AxiosResponse } from './types'
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise(resolve => {
const { data = null, url, method = 'get', headers, responseType } = config
const request = new XMLHttpRequest()
if (responseType) {
request.responseType = responseType
}
request.open(method.toUpperCase(), url, true)
request.onreadystatechange = function handleLoad() {
if(request.readyState !== 4) {
return
}
const responseHeaders = request.getAllResponseHeaders()
const responseData = responseType && responseType !== 'text' ? request.response : request.responseText
const response: AxiosResponse = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config,
request
}
resolve(response)
}
Object.keys(headers).forEach(name => {
if (data === null && name.toLowerCase() === 'content-type') {
delete headers[name]
} else {
request.setRequestHeader(name, headers[name])
}
})
request.send(data)
})
}
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
- 需要判断 如果
config
配置了responseType
,需要将它设置到xhr
的requset.responseType
;- 在
onreadystatechange
事件函数中,构造了AxiosResponse
类型的response
对象,并把它resolve
出去。
修改了 xhr
函数,同样要修改对应的 axios
函数:
src/index.ts
:
function axios(config: AxiosRequestConfig): AxiosPromise {
processConfig(config)
return xhr(config)
}
2
3
4
至此实现了
axios
的Promise
化。
# 编写测试 DEMO
examples/base/app.ts
,添加:
axios({
method: 'post',
url: '/base/post',
data: {
a: 1,
b: 2
}
}).then((res) => {
console.log(res)
})
axios({
method: 'post',
url: '/base/post',
responseType: 'json',
data: {
a: 3,
b: 4
}
}).then((res) => {
console.log(res)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
运行后可以看到可以正常
log
输出这个res
响应对象, 其中包含AxiosRepsonse
类型定义的那些属性,不过发现两个问题:
- 第一个
headers
属性是一个字符串,需要解析为 JSON 对象;- 第一个请求中,
res
响应对象中的data
得到的数据是一个 JSON 字符串,需要转换为对象类型。