2010-12-17 83 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" />