问题
访问链接http://localhost:8080/spring03/requestParam7?addresses[0].city=beijing
时,页面显示Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
意思为:在请求目标中找到无效字符,有效字符在RFC 7230和RFC 3986中定义。
经了解,这个问题是高版本tomcat中的新特性:就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-z A-Z
)、数字(0-9
)、- _ . ~
4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]
)。
RFC3986文档对Url的编解码问题做出了详细的建议,指出了哪些字符需要被编码才不会引起Url语义的转变,以及对为什么这些字符需要编码做出了相应的解释。
- 在US-ASCII字符集中没有的可打印字符:Url中只允许使用可打印字符。US-ASCII码中的10-7F字节全都表示控制字符,这些字符都不能直接出现在Url中。同时,对于80-FF字节(ISO-8859-1),由于已经超出了US-ACII定义的字节范围,因此也不可以放在Url中。
- 保留字符:Url可以划分成若干个组件,协议、主机、路径等。有一些字符(
: / ? # [ ] @
)是用作分隔不同组件的。例如:冒号用于分隔协议和主机,/用于分隔主机和路径,?用于分隔路径和查询参数,等等。还有一些字符(! $ & ' ( ) * + , ; =
)用于在每个组件中起到分隔作用的,如=用于表示查询参数中的键值对,&符号用于分隔查询多个键值对。当组件中的普通数据包含这些特殊字符时,需要对其进行编码。
如果需要在URL中用到特殊字符,需要将这些特殊字符换成相应的十六进制的值
-
不安全字符
:有一些字符,当他们直接放在Url中的时候,可能会引起解析程序的歧义。这些字符被视为不安全字符,原因有很多。 -
空格
:Url在传输的过程,或者用户在排版的过程,或者文本处理程序在处理Url的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉。 -
引号以及<>
:引号和尖括号通常用于在普通文本中起到分隔Url的作用 -
#
:通常用于表示书签或者锚点 -
%
:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码 -
{ } | \ ^ [ ]
~ `:某一些网关或者传输代理会篡改这些字符
需要注意的是,对于Url中的合法字符,编码和不编码是等价的,但是对于上面提到的这些字符,如果不经过编码,那么它们有可能会造成Url语义的不同。因此对于Url而言,只有普通英文字符和数字,特殊字符$-_.+!*'()
还有保留字符,才能出现在未经编码的Url之中。其他字符均需要经过编码之后才能出现在Url中。
但是由于历史原因,目前尚存在一些不标准的编码实现。例如对于~
符号,虽然RFC3986文档规定,对于波浪符号~,不需要进行Url编码,但是还是有很多老的网关或者传输代理会。
我的url为http://localhost:8080/spring03/requestParam7?addresses[0].city=beijing&addresses[1].province=hebei
由此我知道我需要将url中的[]
符号进行url编码([
为%5b
]
为%5d
)才可以使用,由此我将上述url改为以下地址后,成功显示http://localhost:8080/spring03/requestParam7?addresses%5b0%5d.city=beijing&addresses%5b1%5d.province=hebei
解决办法
综上,有3种解决方案:
- 使用Tomcat7.0.69之前的版本;
- 对url的特殊字符进行转义
- 修改tomcat配置文件
方法一:使用Tomcat7.0.69之前的版本;
方法二:对url的特殊字符进行转义
URL中只能使用US-ASCII字符集可以显示的字符,如果需要在URL中使用不属于此字符集的字符,就要使用特殊的符号对该字符进行编码,如最常使用的空格用%20来表示。
除了那些无法显示的字符外,还需要在URL中对那些保留(reserved)字符和不安全(unsafe)字符进行编码。
保留字符是指那些在URL中具有特定意义的字符,不安全字符是指那些在URL中没有特殊含义,但在URL所在的上下文中可能具有特殊意义的字符。
所有可以在浏览器上对特殊字符进行url编码,对应编码对应如下
部分保留字符和不安全字符及其URL编码
字符 | 描述 | 用法 | 编码 |
---|---|---|---|
; | 分号 | 保留 | %3B |
/ | 斜线 | 保留 | %2F |
? | 问号 | 保留 | %3F |
: | 冒号 | 保留 | %3A |
@ | “at”符号 | 保留 | %4O |
= | 等号 | 保留 | %3D |
& | “和”符号 | 保留 | %26 |
< | 小于号 | 不安全 | %3C |
> | 大于号 | 不安全 | %3E |
“ | 双引号 | 不安全 | %22 |
# | 井号 | 不安全 | %23 |
% | 百分号 | 不安全 | %25 |
{ | 左大括号 | 不安全 | %7B |
} | 右大括号 | 不安全 | %7D |
| | 竖线 | 不安全 | %7C |
\ | 反斜线 | 不安全 | %5C |
^ | 加字号 | 不安全 | %5E |
~ | 波浪 | 不安全 | %7E |
[ | 左中括号 | 不安全 | %5B |
] | 右中括号 | 不安全 | %5D |
` | 反单引号 | 不安全 | %60 |
空格 | 不安全 | %20 |
另一种方式,就是发请求前,进行
URLEncoder.encode(requestURI,"UTF-8");
此方法适用于 执行发出请求的是ajax或其他客户端程序(可以发出http请求的编程语言,都有encode()相关支持)。
然后在后台接收请求后decode()
String params = request.getParameter(PARAMS);
params = URLDecoder.decode(params,"UTF-8");
方法三:修改tomcat配置文件
修改tomcat配置文件server.xml,在Connector配置项添加relaxedQueryChars="[]|{}^\`"<>"
这句话就好了,即下面代码块的最后一行是需要添加的。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
relaxedQueryChars="[]|{}^\`"<>"/>
关于\
等编码对应查看如下
https://dev.w3.org/html5/html-author/charref