当前位置 : 首页 > 维修数据

Axios配置增强:支持params与auth参数

<|begin▁of▁sentence|># 1. 前言 在上一篇文章中,我们实现了`axios`的请求和响应配置化,即用户可以配置`config`对象中的`headers`、`data`、`timeout`等属性来改变请求和响应。但是,在之前的实现中,我们允许修改的配置属性还是不够多,所以这篇文章中,我们就来丰富`config`对象中的配置属性。 # 2. 需求分析 根据官方`axios`文档,`axios`支持的配置属性除了之前实现的`url`、`method`、`data`、`headers`、`timeout`、`responseType`之外,还有很多,例如: - `params`:`url`参数,用于`get`请求 - `paramsSerializer`:用于序列化`params` - `auth`:`HTTP`基础验证 - 等等 由于属性太多,我们不可能在这一篇文章中全部实现,所以我们就挑选几个比较常用的来实现,例如:`params`、`auth`等,其余不常用的等后续用到的时候再实现。 # 3. 代码实现 ## 3.1 修改类型定义 由于我们要给`config`对象增加新的属性,所以我们需要先修改之前定义的类型。 我们在`src/types/index.ts`中的`AxiosRequestConfig`中添加如下属性: ```typescript export interface AxiosRequestConfig { // 新增 params?: any; auth?: AxiosBasicCredentials; // 已有 url?: string; method?: Method; data?: any; headers?: any; responseType?: XMLHttpRequestResponseType; timeout?: number; } ``` 其中,`params`属性我们定义为`any`类型,因为它可以是任意类型,例如字符串、对象等。 另外,我们给`auth`属性定义了`AxiosBasicCredentials`类型,该类型我们定义如下: ```typescript export interface AxiosBasicCredentials { username: string; password: string; } ``` ## 3.2 处理 params 参数 我们之前处理`get`请求参数的时候,是将其放在`data`中,然后拼接到`url`上,但是官方`axios`是放在`params`属性中,所以我们也要做同样的处理。 我们在`src/core/xhr.ts`中的`processConfig`函数中,添加对`params`的处理: ```typescript // src/core/xhr.ts function processConfig(config: AxiosRequestConfig): void { config.url = transformUrl(config); config.headers = transformHeaders(config); config.data = transformRequestData(config); } function transformUrl(config: AxiosRequestConfig): string { const { url, params } = config; return buildURL(url, params); } ``` 这里,我们先从`config`中取出`url`和`params`,然后调用`buildURL`函数将`params`参数拼接到`url`上。 ## 3.3 处理 auth 参数 `auth`参数是`HTTP`基础验证,它包含两个属性:`username`和`password`。我们在发送请求的时候,需要将这两个属性按照`HTTP`基础验证的规范,将其拼接成一个字符串,然后放到`headers`的`Authorization`属性中。 我们在`src/core/xhr.ts`中的`processHeaders`函数中,添加对`auth`的处理: ```typescript // src/core/xhr.ts import { isObject, deepMerge } from "../helpers/util"; import { AxiosRequestConfig } from "../types"; import { isPlainObject } from "../helpers/util"; function processHeaders(config: AxiosRequestConfig): void { const { headers = {}, data, auth } = config; // 处理auth if (auth) { headers["Authorization"] = "Basic " + btoa(auth.username + ":" + auth.password); } // 处理Content-Type if (isObject(data)) { if (headers && !headers["Content-Type"]) { headers["Content-Type"] = "application/json;charset=utf-8"; } } return headers; } ``` 这里,我们先从`config`中取出`headers`、`data`和`auth`,然后判断`auth`是否存在,如果存在,就将其按照`HTTP`基础验证的规范,将其拼接成一个字符串,然后放到`headers`的`Authorization`属性中。 ## 3.4 修改 buildURL 函数 我们之前实现的`buildURL`函数只能处理`params`为对象的情况,但是官方`axios`的`params`还可以是`URLSearchParams`、`Array`等类型,所以我们也要做相应的处理。 我们在`src/helpers/url.ts`中的`buildURL`函数中,添加对`params`为`URLSearchParams`、`Array`等类型的处理: ```typescript // src/helpers/url.ts import { isDate, isObject, isURLSearchParams } from "./util"; export function buildURL( url: string, params?: any, paramsSerializer?: (params: any) => string ): string { if (!params) { return url; } let serializedParams; if (paramsSerializer) { serializedParams = paramsSerializer(params); } else if (isURLSearchParams(params)) { serializedParams = params.toString(); } else { const parts: string[] = []; Object.keys(params).forEach((key) => { const val = params[key]; if (val === null || typeof val === "undefined") { return; } let 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)}`); }); }); serializedParams = parts.join("&"); } if (serializedParams) { const markIndex = url.indexOf("#"); if (markIndex !== -1) { url = url.slice(0, markIndex); } url += (url.indexOf("?") === -1 ? "?" : "&") + serializedParams; } return url; } ``` 这里,我们首先判断`params`是否存在,如果不存在,直接返回`url`。 然后,我们判断`paramsSerializer`是否存在,如果存在,就调用`paramsSerializer`函数对`params`进行序列化。 如果`paramsSerializer`不存在,但是`params`是`URLSearchParams`类型,我们就调用`params.toString()`方法将其转换成字符串。 如果`paramsSerializer`不存在,且`params`不是`URLSearchParams`类型,我们就按照之前的处理方式,将其转换成字符串。 最后,我们将序列化后的`params`拼接到`url`上。 另外,我们还需要在`src/helpers/util.ts`中添加`isURLSearchParams`函数: ```typescript // src/helpers/util.ts export function isURLSearchParams(val: any): val is URLSearchParams { return typeof val !== "undefined" && val instanceof URLSearchParams; } ``` ## 3.5 修改 transformHeaders 函数 我们之前实现的`transformHeaders`函数只能处理`headers`为对象的情况,但是官方`axios`的`headers`还可以是`Headers`、`Array`等类型,所以我们也要做相应的处理。 我们在`src/helpers/headers.ts`中的`transformHeaders`函数中,添加对`headers`为`Headers`、`Array`等类型的处理: ```typescript // src/helpers/headers.ts import { isPlainObject, deepMerge } from "./util"; export function transformHeaders( headers: any, data: any ): any { if (isPlainObject(data)) { if (headers && !headers["Content-Type"]) { headers["Content-Type"] = "application/json;charset=utf-8"; } } return headers; } ``` 这里,我们只处理了`headers`为对象的情况,因为`headers`为`Headers`、`Array`等类型的情况比较少见,所以我们暂时不做处理。 # 4. 测试 ## 4.1 测试 params 参数 我们可以在`examples/base-app.ts`中,添加如下代码: ```typescript // examples/base-app.ts axios({ method: "get", url: "/api/base/get", params: { a: 1, b: 2, }, }); ``` 然后,我们可以在浏览器中查看请求的`url`,可以看到`params`参数已经被拼接到`url`上了。 ![](~@/axios/09/01.png) ## 4.2 测试 auth 参数 我们可以在`examples/base-app.ts`中,添加如下代码: ```typescript // examples/base-app.ts axios({ method: "get", url: "/api/base/get", auth: { username: "admin", password: "123456", }, }); ``` 然后,我们可以在浏览器中查看请求的`headers`,可以看到`Authorization`属性已经被设置成了`Basic YWRtaW46MTIzNDU2`,其中`YWRtaW46MTIzNDU2`是`admin:123456`的`base64`编码。 ![](~@/axios/09/02.png) # 5. 总结 在本篇文章中,我们给`config`对象增加了`params`和`auth`两个属性,并且对`buildURL`函数和`processHeaders`函数做了相应的修改,使其能够处理`params`和`auth`参数。 另外,我们还对`buildURL`函数做了增强,使其能够处理`params`为`URLSearchParams`、`Array`等类型的情况。 在下一篇文章中,我们将实现`axios`的拦截器功能。

栏目列表