Retrieving Response Data
# Retrieving Response Data
# Requirements Analysis
In previous chapters, the requests we sent could receive server-returned data at the network level, but at the code level we didn't handle the returned data at all. We want to process the server response data and support Promise-style chaining:
axios({
method: 'post',
url: '/base/post',
data: {
a: 1,
b: 2
}
}).then((res) => {
console.log(res)
})
2
3
4
5
6
7
8
9
10
We want to access the res object, and we expect it to include: the server-returned data data, the HTTP status code status, the status message statusText, response headers headers, the request configuration object config, and the XMLHttpRequest object instance request.
# Defining Interface Types
Based on the requirements, we can define an AxiosResponse interface type:
export interface AxiosResponse {
data: any
status: number
statusText: string
headers: any
config: AxiosRequestConfig
request: any
}
2
3
4
5
6
7
8
Additionally, since the axios function returns a Promise object, we can define an AxiosPromise interface that extends the generic Promise<AxiosResponse> interface:
export interface AxiosPromise extends Promise<AxiosResponse> {
}
2
This way, when axios returns AxiosPromise type, the parameter in the resolve function will be of AxiosResponse type.
For an AJAX request's response, we can specify the response data type by setting the XMLHttpRequest object's responseType (opens new window) property. So we can add an optional property to the AxiosRequestConfig type:
export interface AxiosRequestConfig {
// ...
responseType?: XMLHttpRequestResponseType
}
2
3
4
The responseType type is XMLHttpRequestResponseType, which is defined as the string literal type "" | "arraybuffer" | "blob" | "document" | "json" | "text".
# Implementing Response Data Retrieval Logic
First, we need to add an onreadystatechange (opens new window) event handler to the xhr function and make the xhr function return AxiosPromise type.
xhr.ts:
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
Note that we also check if responseType is configured in config and set it on request.responseType. In the onreadystatechange event handler, we construct an AxiosResponse type response object and resolve it.
After modifying the xhr function, we also need to update the axios function accordingly:
index.ts:
function axios(config: AxiosRequestConfig): AxiosPromise {
processConfig(config)
return xhr(config)
}
2
3
4
This completes the Promise-based implementation of the axios function.
# Writing the Demo
We add 2 code snippets to 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
When we open the browser and run the demo, we find that we can properly log the res variable, which contains all the properties defined in the AxiosResponse type. However, we notice 2 small issues: first, the headers property is a string and we need to parse it into an object; second, in the first request, the received data is a JSON string that also needs to be converted to an object.
In the next section, we'll solve the first issue by processing the response headers.