🌒 实现处理请求 URL 参数
# 需求分析
根据不同的 URL params 参数有不同的情况分析:
普通参数:
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
;参数值为对象时:
const date = new Date() axios({ method: 'get', url: '/base/get', params: { foo: { bar: 'baz' } } })
1
2
3
4
5
6
7
8
9
10
11最终请求 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 相关的工具。 - 在同目录下创建
util.ts
存放相关的工具;
helpers/util.ts
:检验数据的类型,是否为 Date
对象,或者是普通对象 object
。
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'
}
2
3
4
5
6
7
8
9
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 buildURL(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
}
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
encode()
函数处理特殊字符;buildURL
拼接带有参数的 URL。首先 先遍历params
对象中的键值,判断其中的类型,分别按照不同的类型(数组、 日期、普通对象)编码,然后再添加到一个处理后的参数字符串数组中;使用&
拼接这个字符串数组生成序列化的参数字符串;接着处理哈希标记,并且如果 URL 中已经存在参数,要在其后添加&
再加入字符串处理后的参数。
# 在入口文件引入
import { AxiosRequestConfig } from './types'
import xhr from './xhr'
import { buildURL } from './helpers/url'
function axios(config: AxiosRequestConfig) {
processConfig(config)
xhr(config)
}
function processConfig(config: AxiosRequestConfig) {
config.url = transformURL(config)
}
function transformURL (config: AxiosRequestConfig) {
const {url, params} = config
return buildURL(url, params)
}
export default axios
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在执行 xhr
函数之前,添加处理 config
的方法 processConfig
。在 processConfig
内部,执行 transformURL
处理 URL。
# 编写 DEMO 测试
在 exampls
目录下创建 base
目录:
base/index.html
:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>simple example</title>
</head>
<body>
<script src="/__build__/simple.js"></script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
base/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'
}
})
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
:
router.get('/base/get', function(req, res) {
res.json(req.query)
})
2
3
在 examples/index.html
中添加测试链接:
<li><a href='base'>Base</a></li>
最后进入链接之后,可以看到发送了多个请求,查看请求的 URL 和响应的信息。