### 彪哥

爱好前端

- 初识 TypeScript

- TypeScript 常用语法

- ts-axios 项目初始化

- ts-axios 基础功能实现

  - 处理请求 url 参数
    - 需求分析
      - 参数值为数组
      - 参数值为对象
      - 参数值为 Date 类型
      - 特殊字符支持
      - 空值忽略
      - 丢弃 url 中的哈希标记
      - 保留 url 中已存在的参数
    - buildURL 函数实现
    - 实现 url 参数处理逻辑
    - demo 编写
  - 处理请求 body 数据
  - 处理请求 header
  - 获取响应数据
  - 处理响应 header
  - 处理响应 data

- ts-axios 异常情况处理

- ts-axios 接口扩展

- ts-axios 拦截器实现

- ts-axios 配置化实现

- ts-axios 取消功能实现

- ts-axios 更多功能实现

- ts-axios 单元测试

- ts-axios 部署与发布

- 《TypeScript 从零实现 axios》
- ts-axios 基础功能实现

HuangYi
2020-01-05
目录

# 处理请求 url 参数

# # 处理请求 url 参数

## # 需求分析

还记得我们上节课遗留了一个问题，再来看这个例子：

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        a: 1,
        b: 2
      }
    })

1
2
3
4
5
6
7
8

我们希望最终请求的 `url` 是 `/base/get?a=1&b=2`，这样服务端就可以通过请求的 url 解析到我们传来的参数数据了。实际上就是把 `params` 对象的 key 和 value 拼接到 `url` 上。

再来看几个更复杂的例子。

### # 参数值为数组

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: ['bar', 'baz']
      }
    })

1
2
3
4
5
6
7

最终请求的 `url` 是 `/base/get?foo[]=bar&foo[]=baz'`。

### # 参数值为对象

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: {
          bar: 'baz'
        }
      }
    })

1
2
3
4
5
6
7
8
9

最终请求的 `url` 是 `/base/get?foo=%7B%22bar%22:%22baz%22%7D`，`foo` 后面拼接的是 `{"bar":"baz"}` encode 后的结果。

### # 参数值为 Date 类型

    const date = new Date()

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        date
      }
    })

1
2
3
4
5
6
7
8
9

最终请求的 `url` 是 `/base/get?date=2019-04-01T05:55:39.030Z`，`date` 后面拼接的是 `date.toISOString()` 的结果。

### # 特殊字符支持

对于字符 `@`、`:`、`$`、`,`、``、`[`、`]`，我们是允许出现在 `url` 中的，不希望被 encode。

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: '@:$, '
      }
    })

1
2
3
4
5
6
7

最终请求的 `url` 是 `/base/get?foo=@:$+`，注意，我们会把空格 `` 转换成 `+`。

### # 空值忽略

对于值为 `null` 或者 `undefined` 的属性，我们是不会添加到 url 参数中的。

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: 'bar',
        baz: null
      }
    })

1
2
3
4
5
6
7
8

最终请求的 `url` 是 `/base/get?foo=bar`。

### # 丢弃 url 中的哈希标记

    axios({
      method: 'get',
      url: '/base/get#hash',
      params: {
        foo: 'bar'
      }
    })

1
2
3
4
5
6
7

最终请求的 `url` 是 `/base/get?foo=bar`

### # 保留 url 中已存在的参数

    axios({
      method: 'get',
      url: '/base/get?foo=bar',
      params: {
        bar: 'baz'
      }
    })

1
2
3
4
5
6
7

最终请求的 `url` 是 `/base/get?foo=bar&bar=baz`

## # buildURL 函数实现

根据我们之前的需求分析，我们要实现一个工具函数，把 `params` 拼接到 `url` 上。我们希望把项目中的一些工具函数、辅助方法独立管理，于是我们创建一个 `helpers` 目录，在这个目录下创建 `url.ts` 文件，未来会把处理 `url` 相关的工具函数都放在该文件中。

`helpers/url.ts`：

    import { isDate, isObject } from './util'

    function encode (val: string): string {
      return encodeURIComponent(val)
        .replace(/%40/g, '@')
        .replace(/%3A/gi, ':')
        .replace(/%24/g, '$')
        .replace(/%2C/gi, ',')
        .replace(/%20/g, '+')
        .replace(/%5B/gi, '[')
        .replace(/%5D/gi, ']')
    }

    export function bulidURL (url: string, params?: any) {
      if (!params) {
        return url
      }

      const parts: string[] = []

      Object.keys(params).forEach((key) => {
        let val = params[key]
        if (val === null || typeof val === 'undefined') {
          return
        }
        let values: string[]
        if (Array.isArray(val)) {
          values = val
          key += '[]'
        } else {
          values = [val]
        }
        values.forEach((val) => {
          if (isDate(val)) {
            val = val.toISOString()
          } else if (isObject(val)) {
            val = JSON.stringify(val)
          }
          parts.push(`${encode(key)}=${encode(val)}`)
        })
      })

      let serializedParams = parts.join('&')

      if (serializedParams) {
        const markIndex = url.indexOf('#')
        if (markIndex !== -1) {
          url = url.slice(0, markIndex)
        }

        url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
      }

      return url
    }

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
46
47
48
49
50
51
52
53
54
55

`helpers/util.ts`：

    const toString = Object.prototype.toString

    export function isDate (val: any): val is Date {
      return toString.call(val) === '[object Date]'
    }

    export function isObject (val: any): val is Object {
      return val !== null && typeof val === 'object'
    }

1
2
3
4
5
6
7
8
9
10

## # 实现 url 参数处理逻辑

我们已经实现了 `buildURL` 函数，接下来我们来利用它实现 `url` 参数的处理逻辑。

在 `index.ts` 文件中添加如下代码：

    function axios (config: AxiosRequestConfig): void {
      processConfig(config)
      xhr(config)
    }

    function processConfig (config: AxiosRequestConfig): void {
      config.url = transformUrl(config)
    }

    function transformUrl (config: AxiosRequestConfig): string {
      const { url, params } = config
      return bulidURL(url, params)
    }

1
2
3
4
5
6
7
8
9
10
11
12
13

在执行 `xhr` 函数前，我们先执行 `processConfig` 方法，对 `config` 中的数据做处理，除了对 `url` 和 `params` 处理之外，未来还会处理其它属性。

在 `processConfig` 函数内部，我们通过执行 `transformUrl` 函数修改了 `config.url`，该函数内部调用了 `buildURL`。

那么至此，我们对 `url` 参数处理逻辑就实现完了，接下来我们就开始编写 demo 了。

## # demo 编写

在 `examples` 目录下创建 `base` 目录，在 `base` 目录下创建 `index.html`:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Base example</title>
      </head>
      <body>
        <script src="/__build__/base.js"></script>
      </body>
    </html>

1
2
3
4
5
6
7
8
9
10

接着创建 `app.ts` 作为入口文件：

    import axios from '../../src/index'

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: ['bar', 'baz']
      }
    })

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: {
          bar: 'baz'
        }
      }
    })

    const date = new Date()

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        date
      }
    })

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: '@:$, '
      }
    })

    axios({
      method: 'get',
      url: '/base/get',
      params: {
        foo: 'bar',
        baz: null
      }
    })

    axios({
      method: 'get',
      url: '/base/get#hash',
      params: {
        foo: 'bar'
      }
    })

    axios({
      method: 'get',
      url: '/base/get?foo=bar',
      params: {
        bar: 'baz'
      }
    })

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

接着在 `server.js` 添加新的接口路由：

    router.get('/base/get', function(req, res) {
      res.json(req.query)
    })

1
2
3

然后在命令行运行 `npm run dev`，接着打开 chrome 浏览器，访问 `http://localhost:8080/` 即可访问我们的 demo 了，我们点到 `Base` 目录下，通过开发者工具的 network 部分我们可以看到成功发送的多条请求，并可以观察它们最终请求的 url，已经如期添加了请求参数。

那么至此我们的请求 `url` 参数处理编写完了，下一小节我们会对 `request body` 数据做处理。

编辑
(opens new window)
#TypeScript
上次更新: 2022/12/14, 19:36:42
编写基础请求代码
处理请求 body 数据

← 编写基础请求代码 处理请求 body 数据→

最近更新

  01
Git修改分支名
08-11

  02
CSS给table的tbody添加滚动条
06-29

  03
我做了一个手写春联小网页，祝大家虎年暴富 原创
01-28

更多文章>

Copyright © 2016-2022 Hesb | MIT License

- 跟随系统
- 浅色模式
- 深色模式
- 阅读模式