Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring/Rest @PathVariable character encoding

In the environment I'm using (Tomcat 6), percent sequences in path segments apparently are decoded using ISO-8859-1 when being mapped to a @PathVariable.

I'd like that to be UTF-8.

I already configured Tomcat to use UTF-8 (using the URIEncoding attribute in server.xml).

Is Spring/Rest doing the decoding on its own? If yes, where can I override the default encoding?

Additional information; here's my test code:

@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 );
}

If I do an HTTP GET request with the following URI path:

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

which is the UTF-8 encoded then percent-encoded form of

/TEST/enc/£ and € rates

the output that I get is:

      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

which to me shows that Tomcat (after setting the URIEncoding attribute) does the right thing (see getPathInfo()), but the path variable is decoded still in ISO-8859-1.

And the answer is:

Spring/Rest apparently uses the request encoding, which is a very strange thing to do, as this is about the body, not the URI. Sigh.

Adding this:

<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>

fixed the problem. It really should be simpler.

And actually, it's worse:

If the method indeed has a request body, and that one isn't encoded in UTF-8, the additional forceEncoding parameter is needed. This seems to work, but I'm concerned it will cause more problems later on.

Another approach

In the meantime, I found out that it's possible to disable the decoding, my specifying

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

...in which case the recipient can to the right thing; but of course this will make lots of other things harder.

like image 711
Julian Reschke Avatar asked Dec 17 '10 13:12

Julian Reschke


People also ask

What is the use of @PathVariable annotation in Spring rest?

The @PathVariable annotation is used to extract the value of the template variables and assign their value to a method variable. A Spring controller method to process above example is shown below; @RequestMapping("/users/{userid}", method=RequestMethod.

What is @PathVariable in Spring?

@PathVariable is a Spring annotation which indicates that a method parameter should be bound to a URI template variable. If the method parameter is Map<String, String> then the map is populated with all path variable names and values. It has the following optional elements: name - name of the path variable to bind to.

How do I pass a path variable in rest API?

The @PathVariable annotation is used to extract the value from the URI. It is most suitable for the RESTful web service where the URL contains some value. Spring MVC allows us to use multiple @PathVariable annotations in the same method. A path variable is a critical part of creating rest resources.

How do you make a path variable mandatory?

Using @PathVariable required attribute 3 version, @PathVariable annotation has required attribute, to specify it is mandatorily required in URI. The default value for this attribute is true if we make this attribute value to false, then Spring MVC will not throw an exception.


3 Answers

I thing that you need add filter to 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> 
like image 199
Hurda Avatar answered Sep 22 '22 19:09

Hurda


The path variable is still decoded in ISO-8859-1 for me, even with the Character Encoding Filter. Here is what I had to do to get around this. Please let me know if you have any other ideas!

To see the actual UTF-8 decoded characters on the server, you can just do this and take a look at the value (you need to add "HttpServletRequest httpServletRequest" to your controller parameters):

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

I can then do whatever I want (like get the parameter manually from the decoded URI), now that I have the right decoded data on the server.

like image 31
11101101b Avatar answered Sep 21 '22 19:09

11101101b


Try to configure connector on Tomcat in server.xml. Add useBodyEncodingForURI="true" or URIEncoding="UTF-8" to your Connector tag. For example:

    <Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           useBodyEncodingForURI="true"
           redirectPort="8443" />
like image 23
Avseiytsev Dmitriy Avatar answered Sep 20 '22 19:09

Avseiytsev Dmitriy