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干了
相关推荐
脑袋大大的26 分钟前
判断当前是否为钉钉环境
开发语言·前端·javascript·钉钉·企业应用开发
军军君0135 分钟前
基于Springboot+UniApp+Ai实现模拟面试小工具二:后端项目搭建
前端·javascript·spring boot·spring·微信小程序·前端框架·集成学习
江城开朗的豌豆3 小时前
退出登录后头像还在?这个缓存问题坑过多少前端!
前端·javascript·vue.js
江城开朗的豌豆3 小时前
Vue的'读心术':它怎么知道数据偷偷变了?
前端·javascript·vue.js
江城开朗的豌豆3 小时前
手把手教你造一个自己的v-model:原来双向绑定这么简单!
前端·javascript·vue.js
江城开朗的豌豆3 小时前
v-for中key值的作用:为什么我总被要求加这个'没用的'属性?
前端·javascript·vue.js
goldenocean3 小时前
React之旅-05 List Key
前端·javascript·react.js
Mintopia5 小时前
像素的进化史诗:计算机图形学与屏幕的千年之恋
前端·javascript·计算机图形学
Mintopia5 小时前
Three.js 中三角形到四边形的顶点变换:一场几何的华丽变身
前端·javascript·three.js