2010-12-17 49 views
23

在我正在使用的环境(Tomcat 6)中,路径段中的百分比序列显然在映射到@PathVariable时使用ISO-8859-1解码。Spring/Rest @PathVariable字符编码

我希望这是UTF-8。

我已将Tomcat配置为使用UTF-8(使用server.xml中的URIEncoding属性)。

Spring/Rest是否自行解码?如果是,我可以在哪里覆盖默认编码?

其他信息;这里是我的测试代码:

@RequestMapping(value = "/enc/{foo}", method = RequestMethod.GET) 
public HttpEntity<String> enc(@PathVariable("foo") String foo, HttpServletRequest req) 
{ 
    String resp; 

    resp = "  path variable foo: " + foo + "\n" + 
     "  req.getPathInfo(): " + req.getPathInfo() + "\n" + 
     "req.getPathTranslated(): " + req.getPathTranslated() + "\n" + 
     " req.getRequestURI(): " + req.getRequestURI() + "\n" + 
     " req.getContextPath(): " + req.getContextPath() + "\n"; 

    HttpHeaders headers = new HttpHeaders(); 
    headers.setContentType(new MediaType("text", "plain", Charset.forName("UTF-8"))); 
    return new HttpEntity<String>(resp, headers); 
} 

如果我做了以下URI路径的HTTP GET请求:

/TEST/enc/%c2%a3%20and%20%e2%82%ac%20rates 

这是UTF-8编码,然后百分比编码的

/TEST/enc/£ and € rates 
形式

我得到的输出是:

 path variable foo: £ and ⬠rates 
     req.getPathInfo(): /enc/£ and € rates 
req.getPathTranslated(): C:\Users\jre\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\TEST\enc\£ and € rates 
    req.getRequestURI(): /TEST/enc/%C2%A3%20and%20%E2%82%AC%20rates 
    req.getContextPath(): /TEST 

对我来说,这表明Tomcat(在设置URIEncoding属性后)做了正确的事情(请参阅getPathInfo()),但路径变量仍在ISO-8859-1中解码。

答案是

春/休息显然使用请求编码,这是一件非常奇怪的事情,因为这是关于,而不是URI。叹。

添加此:

<filter> 
    <filter-name>CharacterEncodingFilter</filter-name> 
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
    <init-param> 
     <param-name>encoding</param-name> 
     <param-value>UTF-8</param-value> 
    </init-param> 
</filter> 
<filter-mapping> 
    <filter-name>CharacterEncodingFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

解决了这一问题。它确实应该更简单。

而实际上,它更糟糕的是:

如果方法确实请求主体,而在UTF-8,一个是没有编码,需要附加forceEncoding参数。这似乎有效,但我担心它会在以后引发更多问题。

另一种方法

在此期间,我发现它可以禁用解码,我指定

<property name="urlDecode" value="false"/> 

...在这种情况下,收件人可向正确的事;但当然这会使很多其他事情变得更加困难。

回答

27

我的事情,你需要添加筛选到web.xml

<filter> 
    <filter-name>CharacterEncodingFilter</filter-name> 
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
    <init-param> 
     <param-name>encoding</param-name> 
     <param-value>UTF-8</param-value> 
    </init-param> 
    <init-param> 
     <param-name>forceEncoding</param-name> 
     <param-value>true</param-value> 
    </init-param> 
</filter> 
<filter-mapping> 
    <filter-name>CharacterEncodingFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 
+1

这在理论上听起来不错,但似乎没有帮助。查看文档,如果强制执行* body *的编码,而不是URI。 – 2010-12-17 15:41:36

+1

@Julian:这是一个正确的解决方案(尽管'forceEncoding'没有必要),Spring使用请求编码来解析路径变量,参见http://static.springsource.org/spring/docs/3.0.x/javadoc-api /org/springframework/web/util/UrlPathHelper.html(无论如何你也需要这个POST参数过滤器)。 – axtavt 2010-12-19 18:51:32

+1

@axtavt:哦,我的,谁想出这样的设计?无论如何,当我使用UTF-8编码体(如POST)发送HTTP请求时,我已经能够确认我确实获得了UTF-8。我*无法*得到过滤器工作广告(我知道发生了什么事情,因为当我打破类名时,我得到一个ClassNotFoundException)。 – 2010-12-19 19:45:06

4

PATH变量仍解码ISO-8859-1对我来说,即使有字符编码过滤器。这是我必须做的,以解决这个问题。请让我知道,如果你有任何其他的想法!

要看到实际的UTF-8解码的服务器上的字符,你可以做到这一点,看看值(你需要添加“HttpServletRequest的HttpServletRequest的”你的控制器参数):

String requestURI = httpServletRequest.getRequestURI(); 
String decodedURI = URLDecoder.decode(requestURI, "UTF-8"); 

然后我可以做任何我想做的事情(比如从解码的URI手动获取参数),现在我在服务器上拥有正确的解码数据。

+3

确保您的调度servlet的URL映射不会比CharacterEncodingFilter短,否则它甚至不会触及过滤器。 – checketts 2012-02-27 21:07:36

+0

这就是问题所在!谢谢! – 11101101b 2012-02-28 23:18:02

0

但是,难道你不得不惹恼Tomcat配置(URIEncoding)来完成这项工作吗?如果servlet API提供了一种在其未解码表示中获取路径和请求参数的方法,则应用程序(或Spring)可以完全独立处理解码。显然,HttpServletRequest#getPathInfoHttpServletRequest#getQueryString甚至会提供这个,但对于后者,这意味着Spring将不得不解析和解码查询字符串本身,而不依赖于HttpServletRequest#getParameter和朋友。显然他们不这样做,这意味着你不能依赖于servlet容器的配置,安全地使用除了us-ascii字符串之外的任何东西,即@RequestParam@PathVariable

2

尝试在server.xml中的T​​omcat上配置连接器。 将useBodyEncodingForURI="true"URIEncoding="UTF-8"添加到您的连接器标签。 例如:

<Connector port="8080" protocol="HTTP/1.1" 
      connectionTimeout="20000" 
      useBodyEncodingForURI="true" 
      redirectPort="8443" />