前言
问题描述
问题编号:18376
- 从实例概览界面的巡检报告进入某个报告详情,显示当前报告缺失。
- 从菜单的巡检报告列表进入同一个报告可以正常显示报告内容
修复方法
研究问题过程中发现导致 url 拼接错误的原因为: 本期重构的顶部的选择框公共组件有一个替换 url 的功能,新的 url 参数转换为 search 字符串的时候使用了URLSearchParams.toString()
方法,此时会将参数中的空格编码为+
,而不是平时常见的%20
。
js
const replaceUrlParams = () => {
const params = new URLSearchParams(location.search);
cascadeSelectOptions.forEach((item) => {
params.delete(item.name);
});
const name = lastOptions.name;
const value = form.getFieldValue(name);
if (value && value !== `${allValuePrefix}${name}`) {
params.set(name, value);
}
history.replace(`${location.pathname}?${params.toString()}`);
};
最终解决方法是手写了一个将URLSearchParams
转换为 string 的方法
js
const uRLSearchParamsToString = (params: URLSearchParams) => {
const searchParamsArray = [];
for (const [paramName, paramValue] of params.entries()) {
searchParamsArray.push(`${paramName}=${encodeURIComponent(paramValue)}`);
}
return searchParamsArray.join('&');
};
问题分析
这个问题也是今天分享的主题:在 http 请求中,空格会被编码成什么?首先用浏览器提供的几个函数试验一下
js
var str='name=black color'
encodeURI(str) //'name=black%20color'
encodeURIComponent(str) //'name%3Dblack%20color'
new URLSearchParams(str).toString() //'name=black+color'
这几个函数有一个共同的功能:对字符串进行编码。但最终编码后的结果是不一样的,不一样的原因是URI 规范和W3C 规范发生了冲突。
URI 中的保留字:

保留字指的是不参与编码的字符。URI 中的保留字分为 2 类:
- 通用分隔符(gen-delims):分隔 URI 的不同组件,比如用于分隔协议、主机、路径、查询等
- 子分隔符(sub-delims):分隔各个组件中提供的附加信息,比如端口号、路径参数等。
URI 的编码规则
除了保留字以外,其他字符的编码都遵循percent-encoding。URI 的字符都可以根据 ASCII 码使用 8 位二进制数表示,然后再将这 8 位二进制数分成 2 组,转换为 16 进制,最后进行拼接。
比如空格字符,在 ASCII 码中对应的二进制表示位0010 0000
,转换为 16 进制就是0x20
,即最终的编码结果就是%20
,16 进制中的数值表示不区分大小写,但为了保持一致,通常都采用大写的形式。
根据percent-encoing 的规则,我们可以发现encodeURIComponent
和encodeURI
的编码结果是完全正确的。既然转换为%20
是正确的,为什么在另外一个方法里又被转换为+
呢?这就不得不提到 HTML form 表单了。
HTML form 表单
在没有 Ajax 之前,前端向后端传参最常见的方法是使用 HTML 表单。在MDN 的 form测试,name 字段输入带空格的字符,可以发现最终的编码结果是name=maste+r&email=yueyueyan1215%40gmail.com
,空格被转换为了加号。因为 form 的 enctype 属性(表单的编码类型)默认是application/x-www-form-urlencoded


此时已经可以确定:URLSearchParams
做 encode 的时候,遵循的就是这个规范。MDN 官方提供的polyfill 代码中也做了映射:
js
var serialize = function (it) {
return replace(encodeURIComponent(it), find, replacer);
};
var find = /[!'()~]|%20/g;
var replacements = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+'
};
var replacer = function (match) {
return replacements[match];
};
关于这种编码方式规范中也有说明:
The application/x-www-form-urlencoded format is in many ways an aberrant monstrosity, the result of many years of implementation accidents and compromises leading to a set of requirements necessary for interoperability, but in no way representing good design practices. In particular, readers are cautioned to pay close attention to the twisted details involving repeated (and in some cases nested) conversions between character encodings and byte sequences. Unfortunately the format is in widespread use due to the prevalence of HTML forms.
大概意思就是这个设计是实现过程中意外和妥协的结果,算不上是好的设计实践。但是因为 HTML 表单导致这个格式被广泛使用,换句话说就是历史包袱太重,改起来的话太麻烦了,所以继续将就着用吧。
总结
- URI 规范里空格编码为
%20
。 x-www-form-urlencoded
中空格被编码为+
- 实际业务开发中之所以很少碰到这个问题是因为使用了axios这种成熟的 HTTP 请求库,这些杂活都被axios干了