处理请求 header

需求分析

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

  1. axios({
  2. method: 'post',
  3. url: '/base/post',
  4. data: {
  5. a: 1,
  6. b: 2
  7. }
  8. })

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

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

  1. axios({
  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 属性,需要自动设置请求 headerContent-Type 字段为:application/json;charset=utf-8

processHeaders 函数实现

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

helpers/headers.ts

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

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

实现请求 header 处理逻辑

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

types/index.ts

  1. export interface AxiosRequestConfig {
  2. url: string
  3. method?: Method
  4. data?: any
  5. params?: any
  6. headers?: any
  7. }

index.ts

  1. function processConfig (config: AxiosRequestConfig): void {
  2. config.url = transformURL(config)
  3. config.headers = transformHeaders(config)
  4. config.data = transformRequestData(config)
  5. }
  6. function transformHeaders (config: AxiosRequestConfig) {
  7. const { headers = {}, data } = config
  8. return processHeaders(headers, data)
  9. }

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

xhr.ts

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

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

demo 编写

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

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

至此我们对于请求的处理逻辑暂时告一段落。目前我们的请求从网络层面是可以收到服务端的响应的,下一节课我们就从代码层面来处理服务端响应,并且让调用方可以拿到从服务端返回的数据。