很多開放平台都是小白開發的,對這個urlencode理解的不到位,他們總是認為java官方的urlencode有bug,需要
URLEncoder.encode("Hello World","UTF-8").replace("+", "%20") 把+號替換成 %20;所以也沒辦法,
看來總之把+號弄成%20能滿足get和post請求,就是對的。URLDecoder.decode("Hello%20World","UTF-8")見下圖,+和%20都會把解碼成空格。可見Java
做的還是不錯的,考慮到了這點。
=======================初步糾結=============================
網上很多人都說:+號 urlencode之后 應該變成%20; 但是后來發現更亂,理不順。
delphi官方的 也挺亂的,總之不安全的字符越來越多。因為后端 越來越多的字符用於表示一種特殊的含義,所以越來越多的字符不安全。
總之這里一團亂麻,維持現狀了 不再深究。
============================2017.03.03 16::09補充 徹底理解===================================
來自:https://www.zhihu.com/question/38753917
--------------------------------------------------
在1994年訂立的RFC1738中。
對字符串中除了-_.三個字符之外的所有非字母數字字符都替換成百分號(%)后跟兩位十六進制數。
十六進制數中字母必須為大寫。
http://tools.ietf.org/html/rfc1738
在2005年定義的RFC3986中,將針對- _.~ (可見又擴充了一個波浪線字符)四個字符之外的所有非字母數字字符進行百分號編碼。
在Java和PHP當中, 由於歷史原因,導致在進行URLEncode的時候,會將空格編碼為+,而不是編碼為十六進制編碼%20
http://tools.ietf.org/html/rfc3895
在php當中也提供了標准的RFC1738的實現
在PHP Manual中有對兩個函數的說明:
urlencode:返回字符串,此字符串中除了 - _ . 之外的所有非字母數字字符都將被替換成百分號(%)后跟兩位十六進制數,空格則編碼為加號(+)。此編碼與 WWW 表單 POST 數據的編碼方式是一樣的,同時與 application/x-www-form-urlencoded 的媒體類型編碼方式一樣。由於歷史原因,此編碼在將空格編碼為加號(+)方面與 RFC1738 編碼(參見 rawurlencode())不同。
rawurlencode:返回字符串,此字符串中除了 - _ . 之外的所有非字母數字字符都將被替換成百分號(%)后跟兩位十六進制數。這是在 RFC 1738 中描述的編碼,是為了保護原義字符以免其被解釋為特殊的 URL 定界符,同時保護 URL 格式以免其被傳輸媒體(像一些郵件系統)使用字符轉換時弄亂。
我自己的理解:
從上面這篇文章中可以看到 很多人都說 java里 的 UrlEncode 把空格編碼成+ 是歷史的原因。且10年前都是這樣說,2005年 java里就是這樣了,現在2017年 都12年過去了 難道還是因為歷史的原因嗎,什么叫做歷史的原因。
再來看另一篇文章:http://blog.csdn.net/sweetsoft/article/details/3081544
---------------------------------------------------------------------------------------------------
在調查一個錯誤的時候,偶然發現HTML頁面中的部分Link含有+,將tag反編譯了一下,發現是因為調用了Java.NET.URLEncoder的方法
public static String encode(String s, String enc)
從代碼中可以很清晰的看到還特別照顧的將' '轉成了'+'。由於印象中的URL編碼規則應該是將空格轉為%20,就google了一把,結果發現很多人都遇到了這個問題,但基本都是語焉不詳。
花了1個多小時仔細搜索了一遍,線索如下:
1、在sun的bug庫中找到有人認為該方法不符合RFC2396標准
http://bugs.sun.com/view_bug.do?bug_id=4616184
sun的回答說不是bug,符合HTML4.01標准
2、在W3C找到HTML標准的說明
http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4
在這里清楚的看到編碼方式是根據ContextType的不同而區別對待的,在form的ContextType是[x-www-form-urlencoded]的時候會對form中的鍵/值對進行編碼,空格被轉義成+,其他字符按照[RFC1738]標准處理成%HH的形式。
3、回頭再看URLEncoder
發現該類的注釋中很明確的寫明了:
converting a String to the application/x-www-form-urlencoded MIME format
從以上的結果看來%20似乎只是在使用上的一個誤解,因為%20可以被解析成空格,所以就理所當然的認為空格應該被轉義為%20。
再進一步,對Java/.Net/JavaScript的相關函數進行下測試,結果發現Java(1.5)與.Net(2.0)的情況一致,但JavaScript的函數還是將空格轉換成了%20。看來這個問題的誤解完全來自於js的錯誤……
=====================================================================
看了這篇文章后 再去理解空格的問題,你會發現有三個個協議標准:RFC1738、RFC2396、HTML4.01;
打開上文中的兩個鏈接來看下:
官方說這不是一個bug,我猜測可能是名字上的叫法 讓人誤解,UrlEncode 應該叫 PostUrlEncode;或者叫WWWFormUrlEncode類。
因為http協議中 有post和get提交:
1.如果是get提交 或者是路徑的話 如: http://www.bai du.com?wo=he he&ni=abc 就應該遵循RFC1738、RFC2396;就變成:http://www.bai%20du.com?wo=he%20he&ni=abc;
2.如果是post提交:因為post提交的參數依然是被弄成鍵值對的方式傳遞的類似GET的QueryString方式,即需要提交的參數應該是: wo=he he&ni=abc;但是由於 html是一種常用語言,它里面有post提價方式,他也有自己的規范,他規定post生成的鍵值對參數中 參數的值如果有空格應該編碼成+號(注意不是%20)見上面鏈接打開后的下圖:
Java官方的URLEncoder.encode 實際上是為了post請求的content-type為x-www-form-urlencoded來設計的。所以沒有什么bug可言。
=============================
結論:
1.資源路徑中含有空格時應該轉碼為%20,
舉例:http://www.baidu.com/he he/index.jsp -----> http://www.baidu.com/he%20he/index.jsp
2.get請求的QueryString里含有空格的話應該轉碼為%20;
舉例:http://www.abc.com?wo=he he ------> http://www.abc.com?wo=he%20he
3.post請求時,content-type = application/x-www-form-urlencoded (一般默認都是這個)時,空格應該轉碼為+;
舉例:向http://www.abc.com/發post請求,參數的值有空格,最終的參數鍵值對是 wo=he+he;
2017-04-12 續集。。。。。。。。
2017.11.05補充。。。。。。。。。。。。。。
get請求的時候可以用,TNetEncoding.URL.EncodeQuery,
post請求的時候可以用,TNetEncoding.URL.EncodeForm
2019-11-03 補充。。。。。。。。。。。。
procedure TfrmAesForm.Button3Click(Sender: TObject); begin { 空格的問題,根據國際URL標准,GET請求是包含在路徑里的,所以與Post請求的標准不同 GET請求參數包含在URL路徑里,他們有一個標准 RFC1738,RFC3986; 此標准要求空格轉為%20 POST請求參數不包含在URL路徑里,他們的參數傳輸有一個自己的標准 application/x-www-form-urlencoded MIME format; 此標准要求空格轉為+ } Memo1.Lines.Add(TNetEncoding.URL.EncodeQuery(' '));//%20 GET請求的參數用這個URLEncode Memo1.Lines.Add(TNetEncoding.URL.EncodeForm(' '));//+ POST請求的參數用這個URLEncode Memo1.Lines.Add(TNetEncoding.URL.Encode(' '));//+ get提交的時候不要用這個,這個會搞成+ end;