java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:479)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
一、爲什麼要編碼轉義
通常如果一樣東西需要編碼,說明這樣東西並不適合傳輸。原因多種多樣,如Size過大,包含隱私數據,對於Url來說,之所以要進行編碼,是因爲Url中有些字符會引起歧義。
例如Url參數字符串中使用key=value鍵值對這樣的形式來傳參,鍵值對之間以&符號分隔,如/s?q=abc&ie=utf-8。如果你的value字符串中包含了=或者&,那麼勢必會造成接收Url的服務器解析錯誤,因此必須將引起歧義的&和=符號進行轉義,也就是對其進行編碼。
又如,Url的編碼格式採用的是ASCII碼,而不是Unicode,這也就是說你不能在Url中包含任何非ASCII字符,例如中文。否則如果客戶端瀏覽器和服務端瀏覽器支持的字符集不同的情況下,中文可能會造成問題。
Url編碼的原則就是使用安全的字符(沒有特殊用途或者特殊意義的可打印字符)去表示那些不安全的字符。
二、哪些字符需要編碼
1、URL特殊字符轉義,URL中一些字符的特殊含義,基本編碼規則如下:
1、空格換成加號(+)
2、正斜槓(/)分隔目錄和子目錄
3、問號(?)分隔URL和查詢
4、百分號(%)制定特殊字符
5、#號指定書籤
6、&號分隔參數
2、不需要編碼的字符:
RFC3986文檔對Url的編解碼問題做出了詳細的建議,指出了哪些字符需要被編碼纔不會引起Url語義的轉變,以及對爲什麼這些字符需要編碼做出了相應的解釋。
1、在US-ASCII字符集中沒有的可打印字符:Url中只允許使用可打印字符。US-ASCII碼中的10-7F字節全都表示控制字符,這些字符都不能直接出現在Url中。同時,對於80-FF字節(ISO-8859-1),由於已經超出了US-ACII定義的字節範圍,因此也不可以放在Url中。
2、保留字符:Url可以劃分成若干個組件,協議、主機、路徑等。有一些字符(:/?#[]@)是用作分隔不同組件的。例如:冒號用於分隔協議和主機,/用於分隔主機和路徑,?用於分隔路徑和查詢參數,等等。還有一些字符(!$&'()*+,;=)用於在每個組件中起到分隔作用的,如=用於表示查詢參數中的鍵值對,&符號用於分隔查詢多個鍵值對。當組件中的普通數據包含這些特殊字符時,需要對其進行編碼。
RFC3986文檔規定,Url中只允許包含以下四種:
1、英文字母(a-zA-Z)
2、數字(0-9)
3、-_.~ 4個特殊字符
4、所有保留字符,RFC3986中指定了以下字符爲保留字符(英文字符): ! * ' ( ) ; : @ & = + $ , / ? # [ ]
3、需要編碼的字符:
如果需要在URL中用到特殊字符,需要將這些特殊字符換成相應的十六進制的值
不安全字符:有一些字符,當他們直接放在Url中的時候,可能會引起解析程序的歧義。這些字符被視爲不安全字符,原因有很多。
- 空格:Url在傳輸的過程,或者用戶在排版的過程,或者文本處理程序在處理Url的過程,都有可能引入無關緊要的空格,或者將那些有意義的空格給去掉。
- 引號以及<>:引號和尖括號通常用於在普通文本中起到分隔Url的作用
- #:通常用於表示書籤或者錨點
- %:百分號本身用作對不安全字符進行編碼時使用的特殊字符,因此本身需要編碼
- {}|\^[]`~:某一些網關或者傳輸代理會篡改這些字符
需要注意的是,對於Url中的合法字符,編碼和不編碼是等價的,但是對於上面提到的這些字符,如果不經過編碼,那麼它們有可能會造成Url語義的不同。因此對於Url而言,只有普通英文字符和數字,特殊字符$-_.+!*'()還有保留字符,才能出現在未經編碼的Url之中。其他字符均需要經過編碼之後才能出現在Url中。
但是由於歷史原因,目前尚存在一些不標準的編碼實現。例如對於~符號,雖然RFC3986文檔規定,對於波浪符號~,不需要進行Url編碼,但是還是有很多老的網關或者傳輸代理會。
三、如何對Url中的非法字符進行編碼
Url編碼通常也被稱爲百分號編碼(Url Encoding,also known as percent-encoding),是因爲它的編碼方式非常簡單,使用%百分號加上兩位的字符——0123456789ABCDEF——代表一個字節的十六進制形式。Url編碼默認使用的字符集是US-ASCII。例如a在US-ASCII碼中對應的字節是0x61,那麼Url編碼之後得到的就是%61,我們在地址欄上輸入http://g.cn/search?q=%61%62%63,實際上就等同於在google上搜索abc了。又如@符號在ASCII字符集中對應的字節爲0x40,經過Url編碼之後得到的是%40。
對於非ASCII字符,需要使用ASCII字符集的超集進行編碼得到相應的字節,然後對每個字節執行百分號編碼。對於Unicode字符,RFC文檔建議使用utf-8對其進行編碼得到相應的字節,然後對每個字節執行百分號編碼。如"中文"使用UTF-8字符集得到的字節爲0xE4 0xB8 0xAD 0xE6 0x96 0x87,經過Url編碼之後得到"%E4%B8%AD%E6%96%87"。
如果某個字節對應着ASCII字符集中的某個非保留字符,則此字節無需使用百分號表示。例如"Url編碼",使用UTF-8編碼得到的字節是0x55 0x72 0x6C 0xE7 0xBC 0x96 0xE7 0xA0 0x81,由於前三個字節對應着ASCII中的非保留字符"Url",因此這三個字節可以用非保留字符"Url"表示。最終的Url編碼可以簡化成"Url%E7%BC%96%E7%A0%81" ,當然,如果你用"%55%72%6C%E7%BC%96%E7%A0%81"也是可以的。
列舉帶有特殊字符的參數替換成另一些代替的參數,如下所示
字符 - URL編碼值
空格 - %20 (URL中的空格可以用+號或者編碼值表示)
" - %22
# - %23
% - %25
& - %26
( - %28
) - %29
+ - %2B
, - %2C
/ - %2F
: - %3A
; - %3B
< - %3C
= - %3D
> - %3E
? - %3F
@ - %40
\ - %5C
| - %7C
{ - %7B
} - %7D
四、具體編碼處理方法
用URLEncode先對你原始url做個編碼,然後使用編碼後的String。
encodeURIComponent(JSON.stringify(files)) 加一下encodeURIComponen 處理即可。
web端:Javascript的escape(),encodeURIComponent(),encodeURI ()這三個函數進行URL編碼,防止特殊字符接收不到。
Java代碼:
五、案例
在將tomcat升級到7.0.81版後,發現系統的有些功能不能使用了,查詢日誌發現是有些地址直接被tomcat認爲存在不合法字符,返回HTTP 400錯誤響應,錯入信息如下:
1、原因分析
經瞭解,這個問題是高版本tomcat中的新特性:就是嚴格按照 RFC 3986規範進行訪問解析,而 RFC 3986規範定義了Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字符以及所有保留字符(RFC3986中指定了以下字符爲保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。而我們的系統在通過地址傳參時,在url中傳了一段json,傳入的參數中有"{"不在RFC3986中的保留字段中,所以會報這個錯。
根據(https://bz.apache.org/bugzilla/show_bug.cgi?id=60594) ,從以下版本開始,有配置項能夠關閉/配置這個行爲:
8.5.x系列的:8.5.12 onwards
8.0.x系列的:8.0.42 onwards
7.0.x系列的:7.0.76 onwards
2、處理方法
.../conf/catalina.properties中,找到最後註釋掉的一行 #tomcat.util.http.parser.HttpParser.requestTargetAllow=| ,改成tomcat.util.http.parser.HttpParser.requestTargetAllow=|{},表示把{}放行
Ps:當然也可以在參數傳遞前對URL先編碼。