问题

访问链接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

【SpringMVC问题】Invalid character found in the request target. The valid characters are defined in RFC_特殊字符
意思为:在请求目标中找到无效字符,有效字符在RFC 7230和RFC 3986中定义。

经了解,这个问题是高版本tomcat中的新特性:就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-z A-Z)、数字(0-9)、- _ . ~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ])。

RFC3986文档对Url的编解码问题做出了详细的建议,指出了哪些字符需要被编码才不会引起Url语义的转变,以及对为什么这些字符需要编码做出了相应的解释。

  1. 在US-ASCII字符集中没有的可打印字符:Url中只允许使用可打印字符。US-ASCII码中的10-7F字节全都表示控制字符,这些字符都不能直接出现在Url中。同时,对于80-FF字节(ISO-8859-1),由于已经超出了US-ACII定义的字节范围,因此也不可以放在Url中。
  2. 保留字符: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种解决方案:

  1. 使用Tomcat7.0.69之前的版本;
  2. 对url的特殊字符进行转义
  3. 修改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="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"这句话就好了,即下面代码块的最后一行是需要添加的。

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
			   relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"/>

关于&#x5c等编码对应查看如下
https://dev.w3.org/html5/html-author/charref