目录

🌒 获取响应数据

# 需求分析

前面的实现中,发送的请求都可以从网络层面接收到服务器端返回的数据;但是代码层面并没有对响应数据进行任何的处理

需要支持对响应数据的处理,并且支持 Promise 的链式调用的方式,如下:

axios({
  method: 'post',
  url: '/base/post',
  headers: {
    'content-type': 'application/json'
  },
  data: {
    a: 1,
    b: 2
  }
}).then((res) => {
  console.log(res)
})
1
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
}
1
2
3
4
5
6
7
8

另外 axios 函数返回的是一个 Promise 对象,可以定义一个 AxiosPromise 接口,继承于 Promise<AxiosResponse> 这个泛型接口:

export interface AxiosPromise extends Promise<AxiosResponse> {
  
}
1
2
3

此时,当 axios 返回的是 AxiosPromise 类型,那么 resolve 函数中的参数是一个 AxiosResponse 类型。

对于 AJAX 请求的 response ,可以指定它的数据类型,通过设置 XMLHttpRequest 对象的 responseType 属性,可以给 axiosRequestConfig 类型添加一个可选属性:

export interface AxiosRequestConfig {
  // ...
  responseType?: XMLHttpRequestResponseType
}
1
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)
  })
}

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
  • 需要判断 如果 config 配置了 responseType ,需要将它设置到 xhrrequset.responseType
  • onreadystatechange 事件函数中,构造了 AxiosResponse 类型的 response 对象,并把它 resolve 出去。

修改了 xhr 函数,同样要修改对应的 axios 函数:

src/index.ts

function axios(config: AxiosRequestConfig): AxiosPromise {
  processConfig(config)
  return xhr(config)
}
1
2
3
4

至此实现了 axiosPromise 化。

# 编写测试 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)
})
1
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 字符串,需要转换为对象类型。
📢 上次更新: 2022/09/02, 10:18:16