http请求中加号被替换为空格?源码背后的秘密

这是why技术的第20篇原创文章
在这里插入图片描述
本周原本是没有时间写技术文章的,为了周更不断,想着去把以前发布在其余平台的一篇原创文章搬过来就行。结果发现,当年我写的那篇文章,离真相还差着十万八千里。html

而去搜索这个问题时,个人文章是检索结果的第一个。
在这里插入图片描述java

原文《http请求参数中加号被替换为空格及请求参数被URLDeCode的记录》连接以下:
https://www.jianshu.com/p/1a3...程序员

因此为了不继续误导读者,就算周末"爆肝",也得输出此文,不得不发。web

这是我做为程序员的自我修养。面试

加号变空格

以前写那篇文章的缘由是碰到了两个有趣的问题,以下:spring

在这里插入图片描述

首先,咱们进行场景复现,搭建项目的过程就不说了,用idea+springboot搭建一个简单的web项目还不是信手拈来的事?apache

在这里插入图片描述

正如上面的现象所示:个人入参是jay+love,可是后台接收到的是jay love,加号变空格了。为何呢?编程

源码之下无秘密

本文分析的Tomcat源码版本为:9.0.29.
在这里插入图片描述浏览器

经过Debug能够找到两处关键的代码:tomcat

第一处:
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....

在这里插入图片描述

从提交时间上能够看出,该问题早在2001年,距今18年前就有人指出来了,并给JDK上报了BUG,他的描述以下:

在这里插入图片描述

首先,咱们先把他的测试代码拿出来跑一下:

在这里插入图片描述

他为何说空格encode以后应该是%20呢?

由于他在BUG里面提到了RFC2396标准。(RFC就不解释了,你只要知道是业界认证的权威标准就行):
地址:http://www.ietf.org/rfc/rfc23...

在这里插入图片描述
在RFC2396的第2.4.1节,明确的说了:"%20"是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呢?

虽然通过试验,'+'和'%20'通过decode都能转化为空格,可是他认为,根据RFC2396来说,这里只能是'%20',怎么能变成'+'呢?因此他以为这是一个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)。
*HTML4.01是1999年12月24日发布的,在HTML4.0基础上进行微小改进,W3C推荐标准 。
在w3c上找到该标准,地址以下
https://www.w3.org/TR/html401...*

下图圈起来的地方很关键,能够点开放大查看:
在这里插入图片描述

找到HTML 4.01第17.13.4节,其中明确指出:当content-type为application/x-www-form-urlencoded时,对names和vaules进行转义,空格用'+'代替。

HTML 4.01第17.13.4节原文以下:
Control names and values are escaped. Space characters are replaced by `+'

官方举的虽然是HTML 4.01的例子,可是我翻译了历史文献,发现其实在更早的HTML 3.2规范中就规定了,HTML 3.2规范在1996年就成为了W3C推荐标准,其中相关内容以下:

连接地址:https://www.w3.org/TR/2018/SP...

在这里插入图片描述

而application/x-www-form-urlencoded是浏览器默认的content-type。

在BUG里面提到的RFC2396标准是1998年8月提出来的

在这里插入图片描述

HTML 3.2规范在1996年就成为了W3C推荐标准。

因此,我以为这就是历史缘由!

再说一次,在HTML 4.01规范中就明确规定了:当content-type为application/x-www-form-urlencoded时,对names和vaules进行转义,空格用'+'代替。

没有缘由,就是规定!我在查询的过程当中发现,其余的编程语言也有这样的问题,由于他们都听从一样的标准,就有了一样的"历史缘由"。

回到前面的这个地方:
在这里插入图片描述

这里解码的时候为何把'+'转化为空格呢?由于"历史缘由",若是URLs中出现了空格,须要用'+'替换,因此这里解码的时候把'+'转化回了空格。先有了编码的操做,因此才会有解码的操做。

不少的文章都在说这是'+'的缘由,甚至有的文章说'+'的编码应该改成%20。可是其实上面分析过了,有问题的是空格,而不是'+'。

那为何咱们在作表单提交的时候,也常常写'+'号呀,为何没有问题呢?

由于当Html的表单被提交时, 每一个表单域都会被Url编码以后才在被发送,下面的小例子能够佐证:

在这里插入图片描述

解决方案

解决方案网上一大堆了,我这里罗列一下吧:

方案一:修改客户端,将客户端带'+'的参数中的'+'所有替换为‍'%2B',以下:

image.png

方案二:修改服务器端,将空格替换为'+',这种方式只适用于参数中'+'没有空格的状况。以下:

在这里插入图片描述

方案三:修改服务器端,将获取参数的方法由‍reuqest.‍getParameter改成‍request.getQueryString(),而后对获得的字符串进行解析。

在这里插入图片描述

最后说一句

正如我文章最开始说的,就算是熬夜爆肝,我也必须得输出这篇文章,由于我最开始的文章不只写的表面,并且还有一些问题,我得对其进行纠正。

让我忽然想起了以前和朋友的一次对话,他问我说:你做为程序员,时刻待命,只要系统一出问题你就立马会响应。你不以为累吗?

我回答道:说真的,当系统出问题,须要我排查问题的时候,我不以为累。由于这个系统是我负责的,代码是我本身一行行的写出来的。出现了问题,我得证实个人系统是没有问题的,是否是别人的打开方式不对。可是若是真的是个人代码致使的问题,我会心有愧疚,我也得当即响应,对其负责。

这是我做为一个程序员的自我修养。

这篇文章的风格和《这道面试题我真不知道面试官想要的回答是什么》有点类似,全文描述的都是很小的知识点,甚至能够说是冷知识。一句话就能说出表面上的为何,提炼出一个知识点。

可是我以为提炼出来的,是一个干瘪瘪的知识点,它不够丰富,没有探索的过程。

而我所展现的是我去寻找这个问题的答案的过程。经过JDK的"BUG"把几个协议串联起来,并且是全世界共同遵循的协议,极具权威性。

才疏学浅,不免会有纰漏,若是你发现了错误的地方,还请你留言给我指出来,我对其加以修改。

感谢您的阅读,感谢您的关注。

以上。

欢迎关注公众号【why技术】。在这里我会分享一些技术相关的东西,主攻java方向,用匠心敲代码,对每一行代码负责。偶尔也会荒腔走板的聊一聊生活,写一写书评,影评。愿你我共同进步。

公众号-why技术

相关文章
相关标签/搜索