整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          http請求中加號被替換為空格?源碼背后的秘密

          http請求中加號被替換為空格?源碼背后的秘密

          是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之后應該是%20呢?

          因為他在BUG里面提到了RFC2396標準。(RFC就不解釋了,你只要知道是業界認證的權威標準就行):

          http://www.ietf.org/rfc/rfc2396.txt

          在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/interact/forms.html#h-17.13.4.1

          下圖圈起來的地方很關鍵,可以點開放大查看:

          找到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/SPSD-html32-20180315/

          而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',如下:

          方案二:修改服務器端,將空格替換為'+',這種方式只適用于參數中'+'沒有空格的情況。如下:

          方案三:修改服務器端,將獲取參數的方法由?reuqest.?getParameter改為?request.getQueryString(),然后對得到的字符串進行解析。

          最后說一句

          正如我文章最開始說的,就算是熬夜爆肝,我也必須得輸出這篇文章,因為我最開始的文章不僅寫的表面,而且還有一些問題,我得對其進行糾正。

          讓我突然想起了之前和朋友的一次對話,他問我說:你作為程序員,時刻待命,只要系統一出問題你就立馬會響應。你不覺得累嗎?

          我回答道:說真的,當系統出問題,需要我排查問題的時候,我不覺得累。因為這個系統是我負責的,代碼是我自己一行行的寫出來的。出現了問題,我得證明我的系統是沒有問題的,是不是別人的打開方式不對。但是如果真的是我的代碼導致的問題,我會心有愧疚,我也得立即響應,對其負責。

          這是我作為一個程序員的自我修養。

          這篇文章的風格和《這道面試題我真不知道面試官想要的回答是什么》有點相似,全文描述的都是很小的知識點,甚至可以說是冷知識。一句話就能說出表面上的為什么,提煉出一個知識點。

          但是我覺得提煉出來的,是一個干癟癟的知識點,它不夠豐富,沒有探索的過程。

          而我所展示的是我去尋找這個問題的答案的過程。通過JDK的"BUG"把幾個協議串聯起來,而且是全世界共同遵循的協議,極具權威性。

          才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,還請你留言給我指出來,我對其加以修改。

          感謝您的閱讀。

          以上。

          么是HTML

          Hyper Text Markup Language, 超文本標記語言

          標記又稱為標簽(Tag), 一般語法:

          <tagName></tagName>

          它可以有屬性(Attribute):

          <tagName attributeName="value">, 如:

          <meta charset="utf-8" />

          標簽也可以不成對地關閉:

          <tagName />

          HTML文檔由瀏覽器解釋并執行。

          HTML文檔基本結構

          <!DOCTYPE html> ----- 告訴瀏覽器用html5的標準來解釋和執行該網頁

          <html>

          <head> ---- 頭部, 可包含meta, title等標簽

          </head>

          <body> ---- 主體, 包含主要內容

          </body>

          </html>

          meta

          <meta charset="utf-8" /> 用于告訴瀏覽器用什么樣的字符編碼來解釋網頁中的文本.

          常見編碼:

          iso-8859-1: 純英文編碼

          gbk, gb2312: 簡體中文編碼

          big5: 大五碼,繁體中文編碼,主要應用于臺灣地區

          utf-8: 國際首選編碼,它兼容所有的字符

          除此之外, meta還可以通過keywords, description屬性對頁面關鍵詞及描述信息進行設置, 以提高搜索引擎的命中.

          title

          網頁標題, 顯示在瀏覽器選項卡的標題欄上!

          文本排版標簽

          h1-h6: 內容標題標簽

          p: 段落

          br: 換行

          hr: 水平線

          strong: 粗體文本

          em: 斜體文本

          span: 無任何特殊樣式的文本

          pre: 預格式標簽,其中的內容在頁面上帶格式渲染

          small: 比當前字體小的文本

          html特殊字符/轉義字符

          空格

          < 小于

          > 大于

          ? 版權符

          " 雙引號

          html注釋

          <!-- 注釋內容 -->

          圖像標簽

          <img

          src="圖像地址"

          title="鼠標懸停提示"

          alt="圖像加載錯誤時的替代文本"

          width="寬度"

          height="高度"

          />

          圖像地址分為2種:

          1. 相對地址, 如: img/cc.jpg

          2. 絕對地址, 如: http://img.bcd.com/2017/1644232421.jpg

          超鏈接


          <a href="鏈接地址" target="目標窗口">文本|圖片</a>

          目標窗口:

          _self: 目標頁面在當前窗口打開

          _blank: 目標頁面在新窗口中打開

          如果是在頁面具有frameset/frame/iframe的場景下:

          _top: 在頂級窗口中打開

          _parent: 在父級窗口中打開

          _自定義名稱: 在指定的特定窗口中打開

          三種用法:

          1. 頁面間鏈接

          <a href="page/login.html"></a>

          2. 錨鏈接

          <a href="#help"></a>

          help是本頁面中一處id為help的標簽, 如: <p id="help">

          或者:

          help是通過a標簽命名的錨記, 如: <a name="help"></a>

          3. 功能性鏈接

          喚醒本地安裝的外部程序如 outlook/foxmail/qq/msn/aliwangwang...

          <a href="mailto:abcdef@qq.com"></a>

          div標簽

          div是一個容器, 常用于頁面的布局

          標簽的分類:

          1. 塊級標簽/塊級元素

          如: div, h1-h6, p, hr

          特征: 獨占容器中的一行, 其寬度是容器的100%

          2. 行級標簽/行級元素

          如: span, img, strong, em, a

          特征1: 多個行級元素可以同處一行, 其寬度由內容來撐開(auto)

          特征2: 大部分行級元素設置其width/height無效

          HBuilder常用快捷鍵

          ctrl + D : 刪除當前行

          ctrl + PgUp : 當前行上移

          ctrl + PgDown : 當前行下移

          ctrl + / : 注釋 | 取消注釋

          ctrl + shift + F : 整理代碼格式

          ctrl + C : 復制當前行

          ctrl + X : 剪切當前行

          ctrl + V : 粘貼

          ctrl + Z : 撤消上一步操作

          ctrl + S : 保存當前文件

          ctrl + shift + S : 保存項目中全部文件

          ctrl + Enter : 在當前行的下方插入新行

          ctrl + shift + Enter : 在當前行的上方插入新行


          以上知識能做的效果圖

          部分效果

          章架構圖

          ?

          完整閱讀本文大約需要二十分鐘時間,可根據文章結構圖直接閱讀自己需要的部分。

          ?


          主站蜘蛛池模板: 日本一区二区三区高清| 久久精品一区二区免费看| 亚洲国产高清在线一区二区三区| 少妇无码一区二区三区免费| 国产成人久久精品区一区二区| 亚洲一区二区三区在线观看网站| 竹菊影视欧美日韩一区二区三区四区五区 | 人妖在线精品一区二区三区| 精品三级AV无码一区| 另类国产精品一区二区| 亚洲一区二区三区国产精品| 亚洲视频在线一区| 国产成人av一区二区三区在线| 亚洲第一区精品日韩在线播放| 无码精品一区二区三区免费视频 | 亚洲AV永久无码精品一区二区国产 | 99久久无码一区人妻a黑| 人妻视频一区二区三区免费 | A国产一区二区免费入口| 夜色阁亚洲一区二区三区| 国产一区二区三区在线视頻| 国精产品一区一区三区免费视频| 久久精品国产一区二区三区日韩| 精品国产一区二区三区久久久狼 | 少妇精品久久久一区二区三区| 国产一区二区中文字幕| 一区二区视频在线播放| 精品人妻中文av一区二区三区| 麻豆天美国产一区在线播放| 2022年亚洲午夜一区二区福利| 亚洲色精品VR一区区三区| 日本成人一区二区三区| 久久国产香蕉一区精品| 国产综合一区二区在线观看 | 91一区二区视频| 一区二区三区在线看| 亚洲.国产.欧美一区二区三区| 一区二区视频在线| 精品无码一区二区三区爱欲| 精品一区二区三区在线观看视频| 国产午夜精品一区二区|