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 的规则,我们可以发现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 表单导致这个格式被广泛使用,换句话说就是历史包袱太重,改起来的话太麻烦了,所以继续将就着用吧。

总结

  1. URI 规范里空格编码为%20。
  2. x-www-form-urlencoded中空格被编码为+
  3. 实际业务开发中之所以很少碰到这个问题是因为使用了axios这种成熟的 HTTP 请求库,这些杂活都被axios干了
相关推荐
今年下半年几秒前
【前端】Vue2 项目 ECharts 统计图表组件封装与使用实践(附组件源码)
前端·javascript·echarts
冒险的豆子5 分钟前
JS 原型链 通俗易懂 解释说明(类比 关系)
开发语言·javascript·原型模式
西索斯coding9 分钟前
在 中接入 教程:p配置、m填写、扩展思考参数一篇就够(2)
java·前端·javascript·ai
用户17335980753728 分钟前
纯前端 PDF 双面打印避坑指南:翻页顺序错了整个白印?
前端·javascript·vue.js
SensorFlow33 分钟前
从神策 SDK 迁移到 SensorFlow:采集层平滑迁移实战
javascript
浮生望2 小时前
从作用域链到 React 陈旧闭包:为什么函数式更新能拿到最新 state
javascript
怕浪猫2 小时前
前端转全栈最低成本的技术栈选择
前端·javascript·面试
WebInfra2 小时前
Rsdoctor 2.0 发布:Agent 驱动的全自动优化工具
前端·javascript·node.js
晨间晓2 小时前
图片到底偏什么颜色?用华为云码道开发 OpenCV 色彩偏移分析工具
javascript·opencv·华为云
鬼手点金11 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw