http 请求中空格的编码

前言

问题描述

问题编号:18376

  1. 从实例概览界面的巡检报告进入某个报告详情,显示当前报告缺失。
  2. 从菜单的巡检报告列表进入同一个报告可以正常显示报告内容

修复方法

研究问题过程中发现导致 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 类:

  1. 通用分隔符(gen-delims):分隔 URI 的不同组件,比如用于分隔协议、主机、路径、查询等
  2. 子分隔符(sub-delims):分隔各个组件中提供的附加信息,比如端口号、路径参数等。

URI 的编码规则

除了保留字以外,其他字符的编码都遵循percent-encoding。URI 的字符都可以根据 ASCII 码使用 8 位二进制数表示,然后再将这 8 位二进制数分成 2 组,转换为 16 进制,最后进行拼接。

比如空格字符,在 ASCII 码中对应的二进制表示位0010 0000,转换为 16 进制就是0x20,即最终的编码结果就是%20,16 进制中的数值表示不区分大小写,但为了保持一致,通常都采用大写的形式。

根据percent-encoing 的规则,我们可以发现encodeURIComponentencodeURI的编码结果是完全正确的。既然转换为%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 表单导致这个格式被广泛使用,换句话说就是历史包袱太重,改起来的话太麻烦了,所以继续将就着用吧。

总结

  1. URI 规范里空格编码为%20
  2. x-www-form-urlencoded中空格被编码为+
  3. 实际业务开发中之所以很少碰到这个问题是因为使用了axios这种成熟的 HTTP 请求库,这些杂活都被axios干了
相关推荐
codingandsleeping5 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
白水清风6 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
用户22152044278007 小时前
new、原型和原型链浅析
前端·javascript
阿星做前端7 小时前
coze源码解读: space develop 页面
前端·javascript
叫我小窝吧7 小时前
Promise 的使用
前端·javascript
前端康师傅8 小时前
JavaScript 作用域
前端·javascript
云枫晖8 小时前
JS核心知识-事件循环
前端·javascript
eason_fan9 小时前
Git 大小写敏感性问题:一次组件重命名引发的CI构建失败
前端·javascript
前端付豪10 小时前
1、震惊!99% 前端都没搞懂的 JavaScript 类型细节
前端·javascript·面试
朝与暮10 小时前
js符号(Symbol)
前端·javascript