开发者工具 · URL 编码器和解码器
Plus 与 %20:应用程序的历史/x-www-form-urlencoded
· 背景
url 编码 html 表单 http 标准
表单将空格编码为 +,而 URI 标准表示为 %20,这是历史原因。这篇文章追溯了从早期 HTML 表单到今天的 WHATWG 定义的约定,并解释了为什么它从未消失。
Plus 与 %20 — 为什么表单和 URI 对空格进行不同的编码
作为 GET 提交的 HTML 表单将空格编码为查询字符串中的加号。在遵循 RFC 3986 的 URL 中,相同的空格将变为 %20。两者都是正确的,因为它们遵循不同的标准。包含空格的表单字段在表单编码中变为 name=value+with+spaces,但在 RFC 3986 中变为 %20。加百分之二十的区别标志着哪个标准适用于您的数据。
表单编码使用加号作为空格,作为 RFC 1866 (1995)、HTML 2.0 的原始表单提交定义的历史约定。 GET 请求将空格编码为加号,将加号保留为文字 + 编码为 %2B。此规则仅适用于 application/x-www-form-urlencoded, 而不是一般 URI 语法。数十亿个服务器框架开始依赖于这一约定。两者的测试显示出明显的差异:表单模式产生加号; URI 模式生成 %20。 URL编码器和解码器提供两种模式可以直接比较。
早期的 HTML 表单和 GET 提交 — 如何定义表单编码以及为什么选择 +
RFC 1866 (1995) 定义表单提交,其中空格变为加号,文字加号变为 %2B。这仅适用于 application/x-www-form-urlencoded. RFC 3986 为一般 URI 语法指定 %20。两种标准故意并存。
RFC 2396 比早期标准更严格地阐明了字符集。它将服务于 URI 结构的保留字符与作为文字数据的非保留字符形式化。标准机构随着浏览器和代理行为的发展而将其规范化。 RFC 3986 后来出现,没有改变编码行为,只是澄清了符号。当前所有浏览器都标准化 UTF-8 编码。 HTML 表单通过提交按钮发送 application/x-www-form-urlencoded 格式,并用加号表示空格。手动 URI 构造使用 %20。了解这两个标准可以防止集成出现意外。
RFC 1866 和更高版本的 HTML 规范 — 规则的编写位置以及它如何与 URI 语法不同
WHATWG URL 标准指定 URLSearchParams.toString() 生成带有空格加号的 application/x-www-form-urlencoded 输出。 URL 构造函数按照 RFC 3986 进行百分比编码。浏览器导航到带有空格编码的 URL %20;作为 GET 提交的表单编码为 plus。这些根本不同的工具有不同的用途。手动encodeURIComponent为空格提供%20 - RFC 3986样式。提交到同一 URL 的表单发送加号。解析表单提交的服务器需要加上;接收 %20 会导致静默参数失败。
测试两者揭示了您所依赖的服务器端假设。 JavaScript URLSearchParams 提供安全的表单样式编码隐藏和复杂性。构造 URLSearchParams,附加条目,调用 toString() 以使用适当的加号获取 application/x-www-form-urlencoded 。或者使用encodeURIComponent构建查询字符串;您会收到 RFC 3986 %20。切勿混合使用方法。手动加上和encodeURIComponent 的查询字符串会产生歧义。接收者无法区分加号是表示空格还是字面上的加号。标准方法处理一致。
当今的 URL 标准 — 应用/x-www-form-urlencoded 作为具有自己规则的单独序列化程序
JSON API 通常拒绝将加号视为空格,根据 RFC 3986 期望 %20。客户端发送 plus 会默默失败:参数消失。使用两种编码测试 API 可以揭示它们接受哪种标准。 JavaScript 中的 URLSearchParams 处理表单编码。 URL 编码器和解码器生成 RFC 3986 %20。
HTML 表单提交会自动处理编码。您的服务器框架决定应用哪些规则。 Rails、Django、PHP 都会自动将接收到的表单数据中的加号视为空格。但为相同端点手动构建查询字符串非常重要。上传的加号会产生歧义。规范合规性和实际服务器行为略有不同。记录您的端点期望的标准。测试两种编码风格。防御性代码可以很好地处理这两个问题。
工作示例:将同一表单字段视为查询字符串和请求正文 — 在一个地方使用 +,在另一个地方使用 %20
JavaScript URLSearchParams 应用表单编码:空格变为加号,而不是 %20。 new URLSearchParams({q: "hello world"}) 生成“q=hello+world”,而不是“q=hello%20world”。这是专门内置于 JavaScript 中的历史 application/x-www-form-urlencoded 规则。但是将此字符串作为原始查询传递给新 URL 会使加号保持为加号;只有 URLSearchParams 将其解码为空格。构造函数忠实于它所看到的。当错误地混合函数时,加号差异会导致常见错误。
URL 构造函数和encodeURIComponent 是不同的工具。 encodeURIComponent 对除了未保留的字母、数字和 - _ 之外的几乎所有内容进行编码。 ! 〜*'()。它假设没有上下文。 URL 构造函数解析实际 URL 并应用每个组件的 WHATWG 规则。 encodeURIComponent 将“hello/world"”转换为“hello%2Fworld”;新 URL 将斜杠视为路径分隔符。相同的输入,不同的输出。通过连接片段构建 URL 时使用encodeURIComponent。使用 URLSearchParams 或 URL 构造函数来获取完整或部分 URL。
为什么它无法修复 - 数十年的服务器和客户端依赖于当前的行为
百分比编码规则从 RFC 1738 (1994) 发展到 RFC 2396 (1998) 到 RFC 3986 (2005)。每一代人都澄清了含糊之处。 RFC 1738 是保守的,对待字符不安全,因为早期的网络对字符的支持有限。部署在 UTF-8 上标准化,实现变得一致。后来的标准放宽了对跨系统证明安全的字符的限制。现代共识:UTF-8 无处不在。标准机构大力维护向后兼容性。修复问题需要全球范围内的协调——三十年后这是不可能的。两种标准有意共存。
使用 plus 和 %20 进行测试揭示了服务器假设。服务器日志显示客户端发送的内容。表格使用加号;手动 URL 使用 %20。根据上下文进行选择并遵循 API 文档。
不涵盖的内容 — multipart/form-data 和 JSON 主体
测试这两种编码可以揭示服务器行为。双向发送 a+b。许多生产服务器都需要表单编码;较新的 API 需要 %20。您的选择取决于接收者的期望。 URLSearchParams 处理表单编码; encodeURIComponent 处理 RFC 编码。
切勿组合编码方法。使用encodeURIComponent %2B 编码然后传递给URLSearchParams 的值将被双重编码为%252B。解码一次会产生 %2B 而不是加号。字符变成文字百分二六字符串而不是加号。检查构建过程中的中间步骤。每个值只发生一次编码。记录您的管道使用哪种编码标准。使用特殊字符进行测试,包括加号、空格、& 符号。
要点:两个标准,在各自的上下文中都是正确的 — URL 编码器和解码器如何为您提供 RFC 3986 形式,其中 %20 表示空格,这样您就知道您正在查看哪一个
加对二十分裂不是一个需要修复的错误。它是以不同方式解决不同问题的标准的历史产物。修复问题需要全球范围内的协调——三十年后这是不可能的。标准机构不会追溯性地破坏网络。 RFC 3986、表单规则、浏览器URL构造各有标准和理由。 RFC 1866 表单编码和 RFC 3986 URI 编码服务于不同的层。故意编码了解您的标准。针对实际有效负载进行测试。
根据上下文选择编码。表单根据 HTML 标准使用 plus。根据 RFC 3986,手动 URI 使用 %20。 API 指定了期望的内容;遵循文档或测试两者。 URL 编码器和解码器显示 RFC 3986。需要表单编码吗? URLSearchParams 就是这样做的。工具不混合编码;了解标准可以防止出现意外。层之间的编码不一致会导致细微的参数丢失、截断、数据损坏。这两个标准在各自的领域都是正确的。谨慎申请并记录。