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. 前言
在第四篇文章(使用Typescript重構axios(三)——實現基礎功能:處理get請求url參數)中,我們編寫了buildURL
函數對get
請求中所攜帶的參數params
根據當參數為數組時、參數為對象時/參數為Date
類型時等不同的情況進行了參數序列化,但是這些參數序列化的規則都是我們自己寫死在代碼里的,還是那句話,我們不可能覆蓋所有的業務場景,換句話說,我們現在寫的這些參數序列化規則不可能適用所有用戶的業務場景,所以我們應該將其設計成可配置的,如果用戶需要自定義請求參數的序列化規則,那么應該允許用戶去自行配置。
在官方axios
中,在請求配置對象中為用戶提供了paramsSerializer
屬性,該屬性值為一個參數序列化函數,用戶可以自定義該函數,從而達到自定義序列化請求參數的目的,官方文檔示例如下:
// `paramsSerializer` is an optional function in charge of serializing `params`
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
// `paramsSerializer`是一個可選函數,負責序列化`params`,
// 如node.js中的qs庫以及jquery中的jquery.param
paramsSerializer: function (params) {
return qs.stringify(params, {arrayFormat: 'brackets'})
},
那么接下來,我們也要為我們的axios
的請求配置對象中添加該屬性,並實現其功能。其實實現起來非常簡單,我們只需在原有的buildURL
中判斷用戶是否配置了paramsSerializer
屬性,如果配置了就調用用戶傳入的序列化函數對參數進行序列化,如果沒有則仍用默認的即可。
2. 向請求配置對象添加屬性
向請求配置對象config
中添加 paramsSerializer
屬性之前,我們需要先在src/types/index.ts
中的配置對象的接口類型定義AxiosRequestConfig
上添加該屬性的定義,如下:
export interface AxiosRequestConfig {
// 新增
paramsSerializer?: (params: any) => string;
}
添加好接口類型定義后,接下里我們就去原有的buildURL
函數去添加相關的判斷邏輯。
3. 修改buildURL函數邏輯
我們只需在src/helpers/url.ts
文件中原有的buildURL
中判斷用戶是否配置了paramsSerializer
屬性,如果配置了就調用用戶傳入的序列化函數對參數進行序列化,如果沒有則仍用默認的即可。修改如下:
import { isURLSearchParams } from "./util";
export function bulidURL(
url: string,
params?: any,
paramsSerializer?: (params: any) => string
) {
// 如果params為空,直接返回原始url
if (!params) {
return url;
}
// 如果url中有哈希標記,則直接返回原始url
if (url.includes("#")) {
const markIndex = url.indexOf("#");
url = url.slice(0, markIndex);
return url;
}
let serializedParams;
if (paramsSerializer) {
serializedParams = paramsSerializer(params);
} else if (isURLSearchParams(params)) {
serializedParams = params.toString();
} else {
// 定義鍵值對數組,用於最后拼接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用'&'拼接
serializedParams = parts.join("&");
}
if (serializedParams) {
// 判斷原始url中是否有已存在的參數,即判斷是否有'?',
// 如果有,則將處理后的鍵值對加'&'拼接在后面,
// 如果沒有,則將處理后的鍵值對加'?'拼接在后面
url += (url.includes("?") ? "&" : "?") + serializedParams;
}
return url;
}
在這里,我們還添加了一個額外的判斷:
if (isURLSearchParams(params)) {
serializedParams = params.toString();
}
我們在src/helpers/utils.ts
文件中編寫了isURLSearchParams
函數,該函數主要用來判斷傳入的參數是否是URLSearchParams
對象實例,如果是的話,那就無需對它進行序列化,直接toString()
轉為字符串即可。
buildURL
函數修改好之后,我們還需要在src/core/dispatchRequest.ts
文件中調用該函數的transformUrl
函數中修改一下調用方式,為它傳入用戶配置的paramsSerializer
屬性。
4. 修改buildURL調用方式
在src/core/dispatchRequest.ts
文件中的transformUrl
函數是用來發送請求前獲取系列化后的參數,我們需要在該函數中拿到用戶配置的paramsSerializer
屬性,然后將其傳給buildURL
函數來序列化參數,最終將序列化后的參數字符串返回。如下:
function transformUrl(config: AxiosRequestConfig): string {
const { url, params, paramsSerializer } = config;
return bulidURL(url!, params, paramsSerializer);
}
OK,這樣我們就把paramsSerializer
屬性以及相關邏輯處理好了,接下來,我們就編寫demo
來測試下效果如何。
5. demo編寫
在 examples
目錄下創建 addParamsSerializer
目錄,在 addParamsSerializer
目錄下創建 index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>addParamsSerializer demo</title>
</head>
<body>
<script src="/__build__/addParamsSerializer.js"></script>
</body>
</html>
接着再創建 app.ts
作為入口文件:
import axios from "../../src/axios";
import qs from "qs";
axios
.get("/api/addParamsSerializer", {
params: new URLSearchParams("a=b&c=d")
})
.then(res => {
console.log(res);
});
axios
.get("/api/addParamsSerializer", {
params: {
a: 1,
b: 2,
c: ["a", "b", "c"]
}
})
.then(res => {
console.log(res);
});
axios
.get("/api/addParamsSerializer", {
params: {
a: 1,
b: 2,
c: ["a", "b", "c"]
},
paramsSerializer: function(params) {
return qs.stringify(params, { arrayFormat: "brackets" });
}
})
.then(res => {
console.log(res);
});
在本demo
中,我們編寫了 3 種情況的請求,第一個滿足請求的 params
參數是 URLSearchParams
對象類型的;第二個請求沒有配置paramsSerializer
屬性,它會按照原來的轉義規則進行序列化,而第三個請求配置了paramsSerializer
屬性,並且為其編寫了自定義的序列化規則,它會按照自定義的序列化規則對參數進行序列化。
接着在 server/server.js
添加新的接口路由:
// 添加paramsSerializer屬性
router.get("/api/paramsSerializer", function(req, res) {
res.end();
});
最后在根目錄下的index.html
中加上啟動該demo
的入口:
<li><a href="examples/addParamsSerializer">addParamsSerializer</a></li>
OK,我們在命令行中執行:
# 同時開啟客戶端和服務端
npm run server | npm start
接着我們打開 chrome
瀏覽器,訪問 http://localhost:8000/ 即可訪問我們的 demo
了,我們點擊 addParamsSerializer
,就可以看到三個請求都已經正常發出,並且打開F12
中的network
可以看到三個請求的參數都分別進行了不同的序列化:
OK,以上就是為我們的axios
添加paramsSerializer
屬性,並實現了其功能。
(完)