处理请求 url 参数
需求分析
还记得我们上节课遗留了一个问题,再来看这个例子:
axios({method: 'get',url: '/base/get',params: {a: 1,b: 2}})
我们希望最终请求的 url 是 /base/get?a=1&b=2,这样服务端就可以通过请求的 url 解析到我们传来的参数数据了。实际上就是把 params 对象的 key 和 value 拼接到 url 上。
再来看几个更复杂的例子。
参数值为数组
axios({method: 'get',url: '/base/get',params: {foo: ['bar', 'baz']}})
最终请求的 url 是 /base/get?foo[]=bar&foo[]=baz'。
参数值为对象
axios({method: 'get',url: '/base/get',params: {foo: {bar: 'baz'}}})
最终请求的 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}})
最终请求的 url 是 /base/get?date=2019-04-01T05:55:39.030Z,date 后面拼接的是 date.toISOString() 的结果。
特殊字符支持
对于字符 @、:、$、,、`、[、],我们是允许出现在url` 中的,不希望被 encode。
axios({method: 'get',url: '/base/get',params: {foo: '@:$, '}})
最终请求的 url 是 /base/get?foo=@:$+,注意,我们会把空格 ` 转换成+`。
空值忽略
对于值为 null 或者 undefined 的属性,我们是不会添加到 url 参数中的。
axios({method: 'get',url: '/base/get',params: {foo: 'bar',baz: null}})
最终请求的 url 是 /base/get?foo=bar。
丢弃 url 中的哈希标记
axios({method: 'get',url: '/base/get#hash',params: {foo: 'bar'}})
最终请求的 url 是 /base/get?foo=bar
保留 url 中已存在的参数
axios({method: 'get',url: '/base/get?foo=bar',params: {bar: 'baz'}})
最终请求的 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 = valkey += '[]'} 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}
helpers/util.ts:
const toString = Object.prototype.toStringexport 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'}
实现 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 } = configreturn bulidURL(url, params)}
在执行 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>
接着创建 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'}})
接着在 server.js 添加新的接口路由:
router.get('/base/get', function(req, res) {res.json(req.query)})
然后在命令行运行 npm run dev,接着打开 chrome 浏览器,访问 http://localhost:8080/ 即可访问我们的 demo 了,我们点到 Base 目录下,通过开发者工具的 network 部分我们可以看到成功发送的多条请求,并可以观察它们最终请求的 url,已经如期添加了请求参数。
那么至此我们的请求 url 参数处理编写完了,下一小节我们会对 request body 数据做处理。
