前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于TypeScript封装Axios笔记(三)

基于TypeScript封装Axios笔记(三)

作者头像
用户7572539
发布2020-08-26 10:00:18
5240
发布2020-08-26 10:00:18
举报
处理请求 header

需求分析

我们上面课遗留了一个问题:

代码语言:javascript
复制
1axios({
2 method: 'post',
3  url: '/base/post',
4  data: {
5    a: 1,
6    b: 2
7  }
8})

我们做了请求数据的处理,把 data 转换成了 JSON 字符串,但是数据发送到服务端的时候,服务端并不能正常解析我们发送的数据,因为我们并没有给请求 header 设置正确的 Content-Type。

所以首先我们要支持发送请求的时候,可以支持配置 headers 属性,如下:

代码语言:javascript
复制
 1axios({
 2 method: 'post',
 3  url: '/base/post',
 4  headers: {
 5 'content-type': 'application/json;charset=utf-8'
 6  },
 7 data: {
 8 a: 1,
 9    b: 2
10  }
11})

并且在当我们传入的 data 为普通对象的时候,headers 如果没有配置 Content-Type 属性,需要自动设置请求 header 的 Content-Type 字段为:application/json;charset=utf-8

processHeaders 函数实现

根据需求分析,我们要实现一个工具函数,对 request 中的 headers 做一层加工。我们在 helpers 目录新建 headers.ts 文件。

helpers/headers.ts:

代码语言:javascript
复制
 1import { isPlainObject } from './util'
 2
 3function normalizeHeaderName (headers: any, normalizedName: string): void {
 4 if (!headers) {
 5 return
 6  }
 7 Object.keys(headers).forEach(name => {
 8 if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
 9      headers[normalizedName] = headers[name]
10 delete headers[name]
11    }
12  })
13}
14
15export function processHeaders (headers: any, data: any): any {
16  normalizeHeaderName(headers, 'Content-Type')
17
18 if (isPlainObject(data)) {
19 if (headers && !headers['Content-Type']) {
20      headers['Content-Type'] = 'application/json;charset=utf-8'
21    }
22  }
23 return headers
24}

这里有个需要注意的点,因为请求 header 属性是大小写不敏感的,比如我们之前的例子传入 header 的属性名 content-type 就是全小写的,所以我们先要把一些 header 属性名规范化。

实现请求 header 处理逻辑

在这之前,我们先修改一下 AxiosRequestConfig 接口类型的定义,添加 headers 这个可选属性:

types/index.ts

代码语言:javascript
复制
1export interface AxiosRequestConfig {
2  url: string
3  method?: Method
4  data?: any
5 params?: any
6  headers?: any
7}

index.ts:

代码语言:javascript
复制
 1function processConfig (config: AxiosRequestConfig): void {
 2 config.url = transformURL(config)
 3 config.headers = transformHeaders(config)
 4 config.data = transformRequestData(config)
 5}
 6
 7function transformHeaders (config: AxiosRequestConfig) {
 8  const { headers = {}, data } = config
 9 return processHeaders(headers, data)
10}

因为我们处理 header 的时候依赖了 data,所以要在处理请求 body 数据之前处理请求 header。

xhr.ts:

代码语言:javascript
复制
 1export default function xhr (config: AxiosRequestConfig): void {
 2 const { data = null, url, method = 'get', headers } = config
 3
 4 const request = new XMLHttpRequest()
 5
 6  request.open(method.toUpperCase(), url, true)
 7
 8 Object.keys(headers).forEach((name) => {
 9 if (data === null && name.toLowerCase() === 'content-type') {
10 delete headers[name]
11    } else {
12      request.setRequestHeader(name, headers[name])
13    }
14  })
15
16  request.send(data)
17}

这里要额外判断一个逻辑,当我们传入的 data 为空的时候,请求 header 配置 Content-Type 是没有意义的,于是我们把它删除。

demo 编写

代码语言:javascript
复制
 1axios({
 2  method: 'post',
 3  url: '/base/post',
 4  data: {
 5    a: 1,
 6    b: 2
 7  }
 8})
 9
10axios({
11  method: 'post',
12  url: '/base/post',
13  headers: {
14 'content-type': 'application/json;'
15  },
16  data: {
17    a: 1,
18    b: 2
19  }
20})
21
22const paramsString = 'q=URLUtils.searchParams&topic=api'
23const searchParams = new URLSearchParams(paramsString)
24
25axios({
26  method: 'post',
27  url: '/base/post',
28  data: searchParams
29})

通过 demo 我们可以看到,当我们请求的数据是普通对象并且没有配置 headers 的时候,会自动为其添加 Content-Type:application/json;charset=utf-8;同时我们发现当 data 是某些类型如 URLSearchParams 的时候,浏览器会自动为请求 header加上合适的 Content-Type。

至此我们对于请求的处理逻辑暂时告一段落‍。处理响应 header。

处理响应 header

需求分析

我们通过 XMLHttpRequest 对象的 getAllResponseHeaders 方法获取到的值是如下一段字符串:

代码语言:javascript
复制
1date: Fri, 05 Apr 2019 12:40:49 GMT
2etag: W/"d-Ssxx4FRxEutDLwo2+xkkxKc4y0k"
3connection: keep-alive
4x-powered-by: Express
5content-length: 13
6content-type: application/json; charset=utf-8

每一行都是以回车符和换行符 \r\n 结束,它们是每个 header 属性的分隔符。对于上面这串字符串,我们希望最终解析成一个对象结构:

代码语言:javascript
复制
1{
2 date: 'Fri, 05 Apr 2019 12:40:49 GMT'
3  etag: 'W/"d-Ssxx4FRxEutDLwo2+xkkxKc4y0k"',
4  connection: 'keep-alive',
5 'x-powered-by': 'Express',
6 'content-length': '13'
7 'content-type': 'application/json; charset=utf-8'
8}

parseHeaders 函数实现及应用

根据需求分析,我们要实现一个 parseHeaders 工具函数。

helpers/headers.ts:

代码语言:javascript
复制
 1export function parseHeaders(headers: string): any {
 2 let parsed = Object.create(null)
 3 if (!headers) {
 4 return parsed
 5  }
 6
 7  headers.split('\r\n').forEach(line => {
 8 let [key, ...vals] = line.split(':')
 9    key = key.trim().toLowerCase()
10 if (!key) {
11 return
12    }
13 const val = vals.join(':').trim()
14    parsed[key] = val
15  })
16
17 return parsed
18}

然后我们使用这个工具函数:

xhr.ts:

1const responseHeaders = parseHeaders(request.getAllResponseHeaders())

接着我们再去看刚才的 demo,发现我们已经把响应的 headers 字段从字符串解析成对象结构了。那么接下来,我们在解决之前遗留的第二个问题:对响应 data 字段的处理。

处理响应 data

需求分析

在我们不去设置 responseType 的情况下,当服务端返回给我们的数据是字符串类型,我们可以尝试去把它转换成一个 JSON 对象。例如:

1data: "{"a":1,"b":2}"

我们把它转换成:

代码语言:javascript
复制
1data: {
2 a: 1,
3  b: 2
4}

transformResponse 函数实现及应用

根据需求分析,我们要实现一个 transformResponse 工具函数。

helpers/data.ts:

代码语言:javascript
复制
 1export function transformResponse(data: any): any {
 2 if (typeof data === 'string') {
 3 try {
 4      data = JSON.parse(data)
 5    } catch (e) {
 6 // do nothing
 7    }
 8  }
 9 return data
10}

index.ts:

代码语言:javascript
复制
 1function axios(config: AxiosRequestConfig): AxiosPromise {
 2  processConfig(config)
 3 return xhr(config).then((res) => {
 4 return transformResponseData(res)
 5  })
 6}
 7
 8function transformResponseData(res: AxiosResponse): AxiosResponse {
 9  res.data = transformResponse(res.data)
10 return res
11}

接着我们再去看刚才的 demo,发现我们已经把响应的 data 字段从字符串解析成 JSON 对象结构了。

那么至此,我们的 ts-axios 的基础功能已经实现完毕。不过到目前为止,我们都仅仅实现的是正常情况的逻辑,下面一章我们要处理各种异常情况的逻辑。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 二少爷的花间集 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档