Axios请求头Content-Type动态设置与规范化处理
<|begin▁of▁sentence|># 1. 前言
在上一篇文章中,我们实现了`axios`的请求和响应配置化,即用户可以配置`config`对象中的`url`、`method`、`params`、`data`、`headers`、`timeout`、`responseType`等属性。其中,`headers`配置对象中的`Content-Type`属性是`axios`内部帮我们自动设置的,而内部是如何判断并设置该属性的呢?这就是本篇文章要讨论的内容。
# 2. 需求分析
在发送`AJAX`请求时,我们可以通过配置`config`对象中的`headers`属性来给请求头添加一些字段,而`headers`属性是个对象,如下:
```javascript
headers: {
'content-type': 'application/x-www-form-urlencoded',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
```
在之前,我们只处理了`headers`中`content-type`属性,并且是写死的,如下:
```typescript
// src/xhr.ts
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, "application/json;charset=utf-8");
}
```
而实际上,`content-type`的值应该根据`data`的数据类型来动态设置,并且用户也可以自行在`headers`中配置`content-type`属性,如果用户配置了该属性,我们就用用户配置的,如果用户没有配置,我们再根据`data`的数据类型来动态设置。
所以,我们需要实现以下需求:
1. 用户如果配置了`headers`中的`content-type`属性,则用用户配置的;
2. 用户如果没有配置`headers`中的`content-type`属性,则根据`data`的数据类型来动态设置;
# 3. 实现思路
根据需求,我们可以写出以下伪代码:
```javascript
if (用户配置了headers中的content-type属性) {
就用用户配置的
} else {
根据data的数据类型来动态设置content-type
}
```
那么,我们该如何判断用户是否配置了`headers`中的`content-type`属性呢?并且,`headers`属性中字段名可能是任意大小写字母,比如`content-type`、`Content-Type`、`CONTENT-TYPE`等等,我们不可能把所有情况都枚举出来,所以我们需要对`headers`属性做一层规范化:将`headers`属性中字段名全部转换成首字母小写,其余单词首字母大写的驼峰格式,例如:将`content-type`转换成`Content-Type`。
规范化之后,我们就可以通过判断`headers`中是否存在`Content-Type`属性来判断用户是否配置了`content-type`属性。
# 4. 代码实现
## 4.1 辅助函数
根据实现思路,我们需要先实现一个规范化`headers`属性中字段名的辅助函数,我们在`src/helpers/headers.ts`中实现该函数:
```typescript
// src/helpers/headers.ts
/**
* 把headers属性中字段名全部转换成首字母小写,其余单词首字母大写的驼峰格式
* @param headers
* @param normalizedName
*/
export function normalizeHeaderName(headers: any, normalizedName: string): void {
if (!headers) {
return;
}
Object.keys(headers).forEach(name => {
if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
headers[normalizedName] = headers[name];
delete headers[name];
}
});
}
```
该函数接收两个参数:`headers`和`normalizedName`,其中`headers`是`config`对象中的`headers`属性,`normalizedName`是我们想要规范化成的字段名,例如`Content-Type`。该函数的作用是:遍历`headers`对象中的所有属性,如果属性名与`normalizedName`不相等,但是转换成大写后与`normalizedName`转换成大写后相等,那么就把该属性名改成`normalizedName`,并删除原来的属性名。
## 4.2 处理请求 headers
接下来,我们在发送请求之前对`config`中的`headers`做一层规范化处理,我们在`src/core/xhr.ts`中实现:
```typescript
// src/core/xhr.ts
import { normalizeHeaderName } from "./helpers/headers";
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise(resolve => {
const {
data = null,
url,
method = "get",
headers,
responseType,
timeout
} = config;
const request = new XMLHttpRequest();
if (responseType) {
request.responseType = responseType;
}
if (timeout) {
request.timeout = timeout;
}
request.open(method.toUpperCase(), url, true);
// 在设置请求header之前先对headers做一层规范化处理
Object.keys(headers).forEach(name => {
// 如果data为null,那么headers中设置content-type是没有意义的,直接删除
if (data === null && name.toLowerCase() === "content-type") {
delete headers[name];
} else {
request.setRequestHeader(name, headers[name]);
}
});
// 规范化headers中的Content-Type属性
normalizeHeaderName(headers, "Content-Type");
request.send(data);
});
}
```
OK,这样我们就实现了对`headers`的规范化处理,并且如果`data`为`null`,那么`headers`中设置`content-type`是没有意义的,我们直接删除。
## 4.3 根据 data 类型动态设置 content-type
接下来,我们就要根据`data`的数据类型来动态设置`content-type`属性了。我们在`src/helpers/headers.ts`中再实现一个辅助函数:
```typescript
// src/helpers/headers.ts
/**
* 根据data的数据类型来动态设置headers中的Content-Type属性
* @param headers
* @param data
*/
export function processHeaders(headers: any, data: any): any {
// 规范化headers中的Content-Type属性
normalizeHeaderName(headers, "Content-Type");
// 如果data是普通对象
if (utils.isObject(data)) {
// 并且如果headers中存在Content-Type属性,那么就用用户设置的
if (headers && !headers["Content-Type"]) {
// 否则,如果没有设置,就默认设置application/json;charset=utf-8
headers["Content-Type"] = "application/json;charset=utf-8";
}
}
return headers;
}
```
该函数接收两个参数:`headers`和`data`,其中`headers`是`config`对象中的`headers`属性,`data`是`config`对象中的`data`属性。该函数的作用是:先对`headers`中的`Content-Type`属性做规范化处理,然后判断`data`是否是普通对象,如果是,并且`headers`中不存在`Content-Type`属性,那么就默认设置`application/json;charset=utf-8`。
然后,我们在发送请求之前调用该函数,对`headers`做处理:
```typescript
// src/core/xhr.ts
import { processHeaders } from "./helpers/headers";
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise(resolve => {
const {
data = null,
url,
method = "get",
headers,
responseType,
timeout
} = config;
// 在处理请求headers之前先对headers做一层处理
const processedHeaders = processHeaders(headers, data);
const request = new XMLHttpRequest();
if (responseType) {
request.responseType = responseType;
}
if (timeout) {
request.timeout = timeout;
}
request.open(method.toUpperCase(), url, true);
// 设置请求头
Object.keys(processedHeaders).forEach(name => {
if (data === null && name.toLowerCase() === "content-type") {
delete processedHeaders[name];
} else {
request.setRequestHeader(name, processedHeaders[name]);
}
});
request.send(data);
});
}
```
OK,这样我们就实现了根据`data`的数据类型来动态设置`content-type`属性。
# 5. 编写 demo
接下来,我们编写 `demo` 来测试下以上功能是否正常。
`examples/more/app.ts`:
```typescript
import axios from "../../src/index";
// 用户配置了headers中的content-type属性
axios({
method: "post",
url: "/api/more/post",
data: {
a: 1
},
headers: {
"content-type": "application/x-www-form-urlencoded",
Accept: "application/json, text/plain, */*"
}
}).then(res => {
console.log(res);
});
// 用户没有配置headers中的content-type属性
axios({
method: "post",
url: "/api/more/post",
data: {
a: 1
}
}).then(res => {
console.log(res);
});
```
我们运行项目后,在浏览器中打开控制台,可以看到两个请求的请求头中`Content-Type`属性分别是:
- 第一个请求:`content-type: application/x-www-form-urlencoded`
- 第二个请求:`Content-Type: application/json;charset=utf-8`


OK,以上结果符合我们的预期,第一个请求使用了用户配置的`content-type`属性,第二个请求根据`data`的数据类型动态设置了`Content-Type`属性。
# 6. 遗留问题
我们虽然实现了根据`data`的数据类型来动态设置`content-type`属性,但是目前只处理了`data`是普通对象的情况,如果`data`是其他类型,比如`URLSearchParams`、`FormData`、`ArrayBuffer`等等,我们还需要根据不同的数据类型来设置不同的`content-type`属性。这个我们会在后面的文章中继续完善。
# 7. 总结
本篇文章我们实现了根据`data`的数据类型来动态设置`content-type`属性,并且用户也可以自行在`headers`中配置`content-type`属性,如果用户配置了该属性,我们就用用户配置的,如果用户没有配置,我们再根据`data`的数据类型来动态设置。
下一篇文章,我们将实现`axios`的响应数据支持泛型。
最新文章
- Go语言基础:变量、常量与流程控制详解
- 自动驾驶技术突破:激光雷达与V2X车联网引领未来交通
- 汽车冷却系统维护保养指南
- 《警惕汽车油管老化:5大危险信号与安全保养指南》
- 超充技术与V2X:重塑未来智能出行的双引擎
- 从“电子乐器”到“数字静音”:NVH电驱如何重塑电动出行体验
- 从“坐船”到“贴地飞行”:悬架优化全攻略
- 电动化浪潮席卷全球,智能驾驶与车联网重塑未来出行
- 保时捷新款跑车速度与激情完美结合
- 汽车变速箱技术全解析:从进化历程到未来趋势
- ESP系统:如何化解转向不足与转向过度的致命危机?
- 发动机驱动汽车前行
- 智能驾驶与新能源技术:车联网、电池与人机交互的未来出行革命
- 固态电池与800V快充:电动车电池技术的三大革命性突破
- V2X车联网技术:激光雷达与毫米波雷达如何重塑智能驾驶
- 汽车与未来出行的绿色革命
- 发动机过热需立即停车检查制动系统确保安全行驶
- 激光雷达与毫米波雷达:自动驾驶技术的核心突破与应用前景
- 汽车湿度传感器故障诊断与维修保养全指南
- 汽车碰撞保险理赔流程详解
