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技術

相關文章
相關標籤/搜索