回到顶部 Fork me on GitHub

使用Typescript重构axios(三)——实现基础功能:处理get请求url参数

0. 系列文章

1.使用Typescript重构axios(一)——写在最前面
2.使用Typescript重构axios(二)——项目起手,跑通流程
3.使用Typescript重构axios(三)——实现基础功能:处理get请求url参数
4.使用Typescript重构axios(四)——实现基础功能:处理post请求参数
5.使用Typescript重构axios(五)——实现基础功能:处理请求的header
6.使用Typescript重构axios(六)——实现基础功能:获取响应数据
7.使用Typescript重构axios(七)——实现基础功能:处理响应header
8.使用Typescript重构axios(八)——实现基础功能:处理响应data
9.使用Typescript重构axios(九)——异常处理:基础版
10.使用Typescript重构axios(十)——异常处理:增强版
11.使用Typescript重构axios(十一)——接口扩展
12.使用Typescript重构axios(十二)——增加参数
13.使用Typescript重构axios(十三)——让响应数据支持泛型
14.使用Typescript重构axios(十四)——实现拦截器
15.使用Typescript重构axios(十五)——默认配置
16.使用Typescript重构axios(十六)——请求和响应数据配置化
17.使用Typescript重构axios(十七)——增加axios.create
18.使用Typescript重构axios(十八)——请求取消功能:总体思路
19.使用Typescript重构axios(十九)——请求取消功能:实现第二种使用方式
20.使用Typescript重构axios(二十)——请求取消功能:实现第一种使用方式
21.使用Typescript重构axios(二十一)——请求取消功能:添加axios.isCancel接口
22.使用Typescript重构axios(二十二)——请求取消功能:收尾
23.使用Typescript重构axios(二十三)——添加withCredentials属性
24.使用Typescript重构axios(二十四)——防御XSRF攻击
25.使用Typescript重构axios(二十五)——文件上传下载进度监控
26.使用Typescript重构axios(二十六)——添加HTTP授权auth属性
27.使用Typescript重构axios(二十七)——添加请求状态码合法性校验
28.使用Typescript重构axios(二十八)——自定义序列化请求参数
29.使用Typescript重构axios(二十九)——添加baseURL
30.使用Typescript重构axios(三十)——添加axios.getUri方法
31.使用Typescript重构axios(三十一)——添加axios.all和axios.spread方法
32.使用Typescript重构axios(三十二)——写在最后面(总结)

项目源码请猛戳这里!!!

1. 前言

在上篇文章中,我们仅仅实现了最基础的发送请求功能,但是发送get请求时携带的参数我们并没有进行处理,发出的请求参数也没有拼接到url上,那么本篇文章我们就来解决这个问题——处理get请求时的url参数。

2. 需求分析

首先,我们先来看下axios官方对get请求中所携带的参数是如何处理的。

2.1 普通参数

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

最终请求的 url/api/handleRequestURL/get?a=1&b=2

2.2 参数值为数组

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

最终请求的 url/api/handleRequestURL/get?foo[]=bar&foo[]=baz

2.3 参数值为对象

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

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

2.4 参数值为 Date 类型

const date = new Date()

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

最终请求的 url/api/handleRequestURL/get?date=2019-07-24T04:46:41.05190Z

2.5 参数值包含特殊字符

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

最终请求的 url/api/handleRequestURL/get?foo=@:$+,注意,空格 会被转换成 +

2.6 参数值包含null或undefined

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

最终请求的 url/api/handleRequestURL/get?foo=bar,对于值为 null 或者 undefined 的属性,会被丢弃。

2.7 url 中存在哈希#标记

axios({
  method: 'get',
  url: '/api/handleRequestURL/get#hash?bar=baz',
  params: {
    foo: 'bar'
  }
})

最终请求的 url/api/handleRequestURL/get,当原始url中存在哈希标记(#)时,所携带的所有参数params会被忽略,并且请求的url不包含#之后的东西。

2.8 url 中已存在的参数

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

最终请求的 url/api/handleRequestURL/get?foo=bar&bar=baz,会把携带的参数拼接到已存在参数的后面。

3. 实现buildURL 函数

根据上面分析,我们需要实现一个工具函数,将各种情况的 params 进行处理并且拼接到原始 url 上。于是我们在src目录下创建 helpers 文件夹,在这个文件夹下创建 url.ts 文件,未来会把处理 url 相关的工具函数都放在该文件中。

// src/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) {
  // 如果params为空,直接返回原始url
  if (!params) {
    return url
  }

  // 如果url中有哈希标记,则直接返回原始url
  if (url.includes('#')) {
    const markIndex = url.indexOf('#')
    url = url.slice(0, markIndex)
    return url
  }
  // 定义键值对数组,用于最后拼接url,将params中的键值对进行处理最终放入parts中,
  // parts最后应该为['key=value','a=1','b=2','c=3',...]
  const parts: string[] = []

  // 遍历params中的键值对
  Object.keys(params).forEach((key) => {
    let val = params[key]
    // 如果有为null或undefined的值,不处理直接跳出循环
    if (val === null || typeof val === 'undefined') {
      return
    }
    let values: string[]
    // 如果值为数组,则将该值赋给临时数组变量values,用于下面遍历处理
    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)}`)
    })
  })

  // 将parts用'&'拼接
  let serializedParams = parts.join('&')

  if (serializedParams) {
    // 判断原始url中是否有已存在的参数,即判断是否有'?',
    // 如果有,则将处理后的键值对加'&'拼接在后面,
    // 如果没有,则将处理后的键值对加'?'拼接在后面
    url += (url.includes('?') ? '&' : '?') + serializedParams
  }

  return url
}

helpers文件夹下创建util.ts,将一些更为通用的工具函数,例如类型判断等放入该文件内。

// src/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 toString.call(val) === '[object Object]'
}

4. 利用buildURL处理原始url

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

index.ts 文件中添加如下代码:

// src/index.ts
import {AxiosRequestConfig} from './types'
import xhr from './xhr'
import {bulidURL} from "./helpers/url";

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);
}

export default axios

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

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

OK,我们对 url 参数处理逻辑就实现完了,接下来我们编写 demo 来试试效果怎么样。

5. 编写demo

examples 目录下创建 handleRequestURL目录,在 handleRequestURL 目录下创建 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>handleRequestURL demo</title>
</head>
<body>
<script src="/__build__/handleRequestURL.js"></script>
</body>
</html>

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

// examples/handleRequestURL/app.ts

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

// 普通参数
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    a: 1,
    b: 2
  }
})

// 参数值为数组
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    foo: ['bar', 'baz']
  }
})

// 参数值为对象
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    foo: {
      bar: 'baz'
    }
  }
})

// 参数值为 Date 类型
const date = new Date()
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    date
  }
})

// 参数值包含特殊字符
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    foo: '@:$, '
  }
})

// 参数值包含null或`undefined`
axios({
  method: 'get',
  url: '/api/handleRequestURL/get',
  params: {
    foo: 'bar',
    baz: null
  }
})

// url 中存在哈希#标记
axios({
  method: 'get',
  url: '/api/handleRequestURL/get#hash?bar=baz',
  params: {
    foo: 'bar'
  }
})

// url 中已存在的参数
axios({
  method: 'get',
  url: '/api/handleRequestURL/get?foo=bar',
  params: {
    bar: 'baz'
  }
})

接着在 server/server.js 添加新的接口路由:

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

最后在根目录下的index.html中加上启动该demo的入口:

<li><a href="examples/handleRequestURL">handleRequestURL</a></li>

OK,我们在命令行中执行:

# 同时开启客户端和服务端
npm run server | npm start

接着我们打开 chrome 浏览器,访问 http://localhost:8000/ 即可访问我们的 demo 了,我们点击 handleRequestURL ,通过F12network 部分我们可以观察发出的请求以及请求的参数。

(完)

posted on 2019-08-19 15:55  难凉热血,码梦为生!  阅读(5460)  评论(0编辑  收藏  举报

导航