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
中的數據做處理,除了對 url
和 params
處理之外,未來還會處理其它屬性。
在 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
,通過F12
的 network
部分我們可以觀察發出的請求以及請求的參數。
(完)