RFC 3986文档规定,Url中只允许包含英文字母(a-z,A-Z)、数字(0-9)、- _ . ~ 4个特殊字符以及所有保留字符。
RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ]
当发送带有特殊字符的url请求(如:http://localhost:8080/test?users[0].age=26&A`12)时,如果不进行encode,就会报错 java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
。
SpringBoot框架中,默认使用的Tomcat 8.5.34版本,该版本Tomcat中已经增加了HTTP请求的规范性校验功能。故HTTP请求中包含不支持的字符如`字符导致出现标题所述的错误:Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986。
信息: Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
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.InternalInputBuffer.parseRequestLine(InternalInputBuffer.java:192)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1028)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
也就是说我们的请求中用了无效的字符。查看RFC规范知,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~四个特殊字符以及保留字符( ! * ’ ( ) ; : @ & = + $ , / ? # [ ] ) (26*2+10+4+18=84)这84个字符.而我们的请求中出现了{}大括号,所以tomcat报错.
解决办法(四种):
- 替换url请求。不用{}
- 对请求编码解码。 UrlDecode、UrlEncode
- 配置Tomcat对字符的支持 (见下文)
-
更换Tomcat版本 (Tomcat从 7.0.73, 8.0.39, 8.5.7 版本后添加了对Url的限制)
-
配置tomcat支持|{}等字符的方法是:在 catalina.properties中添加 tomcat.util.http.parser.HttpParser.requestTargetAllow=|{} 但是只支持7.0.76, 8.0.42, 8.5.12 之后的版本(这些版本之后支持设置上述属性)
-
注,Tomcat从 7.0.73, 8.0.39, 8.5.7 版本后添加了对Url的限制。
-
具体来说,在tomcat源码的org.apache.tomcat.util.http.parser.HttpParser#IS_NOT_REQUEST_TARGET[]
中定义了一堆not request target中
if(IS_CONTROL[i] || i > 127 || i == 32 || i == 34 || i == 35 || i == 60 || i == 62 || i == 92 || i == 94 || i == 96 || i == 123 || i == 124 || i == 125) {
IS_NOT_REQUEST_TARGET[i] = true;
}
转换过来就是以下字符(对应10进制ASCII看):
- 键盘上那些控制键:(
<32或者=127
) - 非英文字符(
>127
) 空格
(32
)双引号
(34
)#
(35
)<
(60
)>
(62
)反斜杠
(92
)^
(94
)TAB上面那个键,我也不晓得嫩个读
(96
){
(123
)}
(124
)|
(125
)
注:通过工具发送请求URL
通过Swagger发送请求会自动将请求编码,不会出现上述问题。
Postman是常用的URL测试工具,可以方便地对URL进行Encode(选中需要Encode的地方,右击),注意!!!Encode的是参数而不是URL!!!(如下图来自于网络)