【问题标题】:Spring/Rest @PathVariable character encodingSpring/Rest @PathVariable 字符编码
【发布时间】:2010-12-17 13:17:50
【问题描述】:

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

我希望它是 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 中解码。

答案是

Spring/Rest 显然使用了请求编码,这是一件很奇怪的事情,因为这是关于 body,而不是 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"/>

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

【问题讨论】:

    标签: spring rest character-encoding uri


    【解决方案1】:

    你需要在 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>
    

    【讨论】:

    • 这在理论上听起来不错,但似乎没有帮助。查看文档,如果强制执行 body 的编码,而不是 URI。
    • @Julian:这是一个正确的解决方案(尽管forceEncoding 不是必需的),Spring 使用请求编码来解析路径变量,请参阅static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…(而且您还需要这个过滤器来处理 POST 参数) )。
    • @axtavt:哦,天哪,谁想出了这样的设计?无论如何,当我发送带有 UTF-8 编码正文(例如 POST)的 HTTP 请求时,我已经能够确认我确实得到了 UTF-8。我没有能够让过滤器像宣传的那样工作(我知道发生了一些事情,因为当我破坏类名时,我得到了 ClassNotFoundException)。
    • @axtavt:哦,我错过了过滤器映射元素。
    • 它不起作用。 Spring MVC 4.0.2 完全注释。无论如何,URL 都是在 ISO-8859-1 中解码的。我必须准备解决方法。路径变量,例如@PathVariable String var,必须首先使用byte[] bytes = var.getBytes("ISO-8859-1"); 解码,然后使用new String(bytes, "UTF-8"); 以UTF-8 编码。
    【解决方案2】:

    即使使用字符编码过滤器,路径变量仍然在 ISO-8859-1 中解码。这是我必须做的才能解决这个问题。如果您有任何其他想法,请告诉我!

    要查看服务器上实际的 UTF-8 解码字符,您可以这样做并查看值(您需要在控制器参数中添加“HttpServletRequest httpServletRequest”):

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

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

    【讨论】:

    • 确保您的调度 servlet 的 URL 映射不短于 CharacterEncodingFilter,否则它甚至不会命中过滤器。
    • 这就是问题所在!谢谢!
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      但是,您必须弄乱 Tomcat 配置 (URIEncoding) 才能完成这项工作,这不是很糟糕吗?如果 servlet API 提供了一种以未解码表示形式获取路径和请求参数的方法,则应用程序(或 Spring)可以完全自行处理解码。显然,HttpServletRequest#getPathInfoHttpServletRequest#getQueryString 甚至会提供这个,但对于后者,这意味着 Spring 必须自己解析和解码查询字符串,而不是依赖于 HttpServletRequest#getParameter 和朋友。显然他们不这样做,这意味着你不能让@RequestParam@PathVariable 在不依赖 servlet 容器配置的情况下安全地捕获除 us-ascii 字符串以外的任何内容。

      【讨论】:

        【解决方案5】:

        今天我尝试在葡萄牙语中使用单词时遇到了这个问题。 SpringBoot 中 Avseiytsev Dmitriy 的answer 可以通过以下方式访问:

        server.tomcat.uri-encoding=UTF-8

        application.properties 文件中

        我已经测试过并且这个作品。

        如果您在应用程序中使用 TDD 并使用 MockMvc 来测试 GET,例如,这样做:

        mockMvc.perform(get("/api/v1/categories/" + NAME2)
                            .characterEncoding("UTF-8")
                            .contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.name", equalTo(NAME2)));
        

        NAME2 变量是一个字符串:José

        【讨论】:

          猜你喜欢
          • 2015-04-30
          • 2014-03-15
          • 2015-08-28
          • 1970-01-01
          • 1970-01-01
          • 2022-06-30
          • 1970-01-01
          • 1970-01-01
          • 2011-07-27
          相关资源
          最近更新 更多