🌒 获取响应数据
# 需求分析
前面的实现中,发送的请求都可以从网络层面接收到服务器端返回的数据;但是代码层面并没有对响应数据进行任何的处理。
需要支持对响应数据的处理,并且支持 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 字符串,需要转换为对象类型。
