自定义参数序列化

需求分析

在之前的章节,我们对请求的 url 参数做了处理,我们会解析传入的 params 对象,根据一定的规则把它解析成字符串,然后添加在 url 后面。在解析的过程中,我们会对字符串 encode,但是对于一些特殊字符比如 @+ 等却不转义,这是 axios 库的默认解析规则。当然,我们也希望自己定义解析规则,于是我们希望 ts-axios 能在请求配置中允许我们配置一个 paramsSerializer 函数来自定义参数的解析规则,该函数接受 params 参数,返回值作为解析后的结果,如下:

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

代码实现

首先修改一下类型定义。

types/index.ts

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

然后修改 buildURL 函数的实现。

helpers/url.ts

  1. export function buildURL(
  2. url: string,
  3. params?: any,
  4. paramsSerializer?: (params: any) => string
  5. ): string {
  6. if (!params) {
  7. return url
  8. }
  9. let serializedParams
  10. if (paramsSerializer) {
  11. serializedParams = paramsSerializer(params)
  12. } else if (isURLSearchParams(params)) {
  13. serializedParams = params.toString()
  14. } else {
  15. const parts: string[] = []
  16. Object.keys(params).forEach(key => {
  17. const val = params[key]
  18. if (val === null || typeof val === 'undefined') {
  19. return
  20. }
  21. let values = []
  22. if (Array.isArray(val)) {
  23. values = val
  24. key += '[]'
  25. } else {
  26. values = [val]
  27. }
  28. values.forEach(val => {
  29. if (isDate(val)) {
  30. val = val.toISOString()
  31. } else if (isPlainObject(val)) {
  32. val = JSON.stringify(val)
  33. }
  34. parts.push(`${encode(key)}=${encode(val)}`)
  35. })
  36. })
  37. serializedParams = parts.join('&')
  38. }
  39. if (serializedParams) {
  40. const markIndex = url.indexOf('#')
  41. if (markIndex !== -1) {
  42. url = url.slice(0, markIndex)
  43. }
  44. url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
  45. }
  46. return url
  47. }

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

helpers/util.ts

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

最后我们要修改 buildURL 调用的逻辑。

core/dispatchRequest.ts

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

demo 编写

  1. axios.get('/more/get', {
  2. params: new URLSearchParams('a=b&c=d')
  3. }).then(res => {
  4. console.log(res)
  5. })
  6. axios.get('/more/get', {
  7. params: {
  8. a: 1,
  9. b: 2,
  10. c: ['a', 'b', 'c']
  11. }
  12. }).then(res => {
  13. console.log(res)
  14. })
  15. const instance = axios.create({
  16. paramsSerializer(params) {
  17. return qs.stringify(params, { arrayFormat: 'brackets' })
  18. }
  19. })
  20. instance.get('/more/get', {
  21. params: {
  22. a: 1,
  23. b: 2,
  24. c: ['a', 'b', 'c']
  25. }
  26. }).then(res => {
  27. console.log(res)
  28. })

我们编写了 3 种情况的请求,第一种满足请求的 params 参数是 URLSearchParams 对象类型的。后两种请求的结果主要区别在于前者并没有对 [] 转义,而后者会转义。

至此,ts-axios 实现了自定义参数序列化功能,用户可以配置 paramsSerializer 自定义参数序列化规则。下一节课我们来实现 ts-axiosbaseURL 的支持。