目录

🌗 实现自定义参数序列化

# 需求分析

目前为止,对于请求的 URL 的参数做了处理,可以解析传入的 params 对象,根据一定的规则把它们解析成字符串,然后添加到 URL 后面。在解析的过程中,会对字符串 encode ,但是对于一些特殊字符却不会转义,这时 axios 默认的解析规则。

当给定自定义的解析规则,希望能在请求配置中允许配置一个 paramsSerializer 函数来自定义参数的解析规则,该函数接受 params 参数,返回值作为解析后的结果。

🌰 使用例子:

axios.get('/more/get', {
  params: {
    a: 1,
    b: 2,
    c: ['a', 'b', 'c']
  },
  paramsSerializer(params) {
    return qs.stringify(params, { arrayFormat: 'brackets' })
  }
}).then(res => {
  console.log(res)
})
1
2
3
4
5
6
7
8
9
10
11
12

# 代码实现

修改 AxiosRequestConfig

export interface AxiosRequestConfig {
  // ...
  paramsSerializer?: (params: any) => string
}
1
2
3
4

然后修改 src/helpers/url.tsbuildURL 函数的实现:

export function buildURL(
  url: string,
  params?: any,
  paramsSerializer?: (params: any) => string
): string {
  if (!params) return url

  let serializedParams
  if(paramsSerializer) {
    serializedParams = paramsSerializer(params)
  } else if (isURLSearchParams(params)) {
    serializedParams = params.toString()
  } else {
    const parts: string[] = []

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

    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

buildURL 函数新增了 paramsSerializer 可选参数。另外还对 params 的类型进行判断,如果是一个 URLSearchParams 对象实例的话, 直接返回它 toString 之后的结果。

src/helpers/util.ts 中:

export function isURLSearchParams(val: any): val is URLSearchParams {
  return typeof val !== 'undefined' && val instanceof URLSearchParams
}
1
2
3

最后在 src/core/dispatchRequest.ts 中修改 buildURL 的逻辑:

function transformURL(config: AxiosRequestConfig): any {
  const { url, params, paramsSerializer } = config
  return buildURL(url, params, paramsSerializer)
}
1
2
3
4

# 编写测试 DEMO

examples/more/app.ts

axios.get('/more/get', {
  params: new URLSearchParams('a=b&c=d')
}).then(res => {
  console.log(res)
})

axios.get('/more/get', {
  params: {
    a: 1,
    b: 2,
    c: ['a', 'b', 'c']
  }
}).then(res => {
  console.log(res)
})

const instance = axios.create({
  paramsSerializer(params) {
    return qs.stringify(params, { arrayFormat: 'brackets' })
  }
})

instance.get('/more/get', {
  params: {
    a: 1,
    b: 2,
    c: ['a', 'b', 'c']
  }
}).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
23
24
25
26
27
28
29
30
31

分别是三种情况,首先第一种满足请求的 params 参数是 URLSearchParams 对象类型的。后两种请求的结果区别在于前者没有对 [] 特殊符号进行转义。

📢 上次更新: 2022/09/02, 10:18:16