这是 why 技术的第 20 篇原创文章
本周本来是没有时间写技术文章的, 为了周更不断, 想着去把之前发布在其他平台的一篇原创文章搬过来就行. 结果发现, 当年我写的那篇文章, 离真相还差着十万八千里.
而去搜索这个问题时, 我的文章是检索结果的第一个.
原文《http 请求参数中加号被替换为空格及请求参数被 URLDeCode 的记录》链接如下:
https://www.jianshu.com/p/1a30b585c39e
所以为了避免继续误导读者, 就算周末 "爆肝", 也得输出此文, 不得不发.
这是我作为程序员的自我修养.
加号变空格
之前写那篇文章的原因是碰到了两个有趣的问题, 如下:
首先, 我们进行场景复现, 搭建项目的过程就不说了, 用 idea+springboot 搭建一个简单的 web 项目还不是信手拈来的事?
正如上面的现象所示: 我的入参是 jay+love, 但是后台接收到的是 jay love, 加号变空格了. 为什么呢?
源码之下无秘密
本文分析的 Tomcat 源码版本为: 9.0.29.
通过 Debug 可以找到两处关键的代码:
第一处:
org.apache.tomcat.util.http.Parameters#processParameters(byte[], int, int, java.nio.charset.Charset) 下图中的 290 行
在这个地方因为有'+', 所以把 decodeValue 参数设置为 true, 表示需要对请求中的 value 进行 decode 操作.
decode 的具体的源码位置如下, 也就是第二处关键代码:
org.apache.tomcat.util.buf.UDecoder#convert(org.apache.tomcat.util.buf.ByteChunk, boolean)
可以看到, 在源码里面有一段代码, 是把'+'替换了为了空格, 是特意做了这样的特殊处理.
整个方法的解读如下:
所以我的入参是 jay+love, 但是后台接收到的是 jay love, 加号变空格了. 为什么呢?
原因很简单, 在源码中有一段代码把'+'替换成了空格, 刻意为之.
为什么这样做呢?
之前的文章里面我写的是:
由于历史原因, 那到底是什么历史原因呢?
我在网上查了一圈, 没有找到具体的历史原因, 我看到的所有的关于这个问题的文章, 要么只是给了解决方案, 要么就是上面这一句历史原因, 一带而过, 含糊其辞.
这里, 我就明明白白的告诉你为啥.
经过我长时间的摸排, 我找到了很多蛛丝马迹, 整理之后, 我决定从 JDK 的一个 "BUG" 讲起.
对应链接: http://bugs.sun.com/view_bug.do?bug_id=4616184
从提交时间上可以看出, 该问题早在 2001 年, 距今 18 年前就有人指出来了, 并给 JDK 上报了 BUG, 他的描述如下:
首先, 我们先把他的测试代码拿出来跑一下:
他为什么说空格 encode 之后应该是 呢?
因为他在 BUG 里面提到了 RFC2396 标准.(RFC 就不解释了, 你只要知道是业界认证的权威标准就行):
http://www.ietf.org/rfc/rfc2396.txt
在 RFC2396 的第 2.4.1 节, 明确的说了:" " 是 US-ASCII 空格字符的转义编码.
去查询标准的 ASCII 码你也可以发现确实是这样的:
用代码实践一下, 证明以上结论:
看 java.NET.URLEncoder#encode(java.lang.String, java.lang.String) 的源码也可以直观的看到, 源码里面做了特殊处理:
再看 java.NET.URLDecoder#decode(java.lang.String, java.lang.String) 的源码:
这里就和前面的呼应上了, 这处理方式, 一模一样呀. 所以为什么这样处理, 两处地方属于同宗同源啊!
而提 BUG 的那个哥们为什么觉得这是一个 BUG 呢?
虽然经过试验,'+'和' '经过 decode 都能转化为空格, 但是他认为, 根据 RFC2396 来讲, 这里只能是' ', 怎么能变成'+'呢? 所以他觉得这是一个 BUG.
那我们看看 JDK 官方是怎么回复这个问题的呢?
官方回复:
这不是 BUG 啊, 朋友! 这个类就是遵循了 html 规范中的规定: 如何对 HTML 表单中的 URLs 进行 encode. 它不打算用于其他用途.
而这样做的原因, 是因为包括 HTML 4.01 第 17.13.4 节和 RFC 1866(已经被 W3C HTML 推荐标准取代) 都是这样规定的.
对于第一段话, 官方的意思我理解是: 这个类就是拿来对 url 进行 encode 的, 不做其他用途. 因为你调用了 encode 编码, 那就需要 decode 解码, 我只要保证你解码之后的数据和你 encode 之前的数据是一样的就行了. 你要拿去搞其他事情, 我就管不了了.
而为什么这样做呢? 是因为规定就是这样的呀, 类似于国家标准就是这样的, 类似于产品经理提出的需求就是这样的呀. 这里官方提出了两个标准, 一个是 HTML 4.01, 一个是 RFC1866(这个已经被其他的标准取代了, 那我们就只看 HTML 4.01).
来源: https://www.cnblogs.com/thisiswhy/p/12119126.html