Saturday, November 15, 2014

Spring MVC ajax request with UTF 8 support

Recently for my Dropbox Writer project, I ran into a issue with transferring none English characters between server and client side. There are not a lot of solutions on the web and those solutions are very inconsistent.

Explain my situation

I created a Spring MVC project what can create text files in Dropbox. Dropbox uses UTF 8 for text encoding, thus it supports non English characters. My application uses ajax to drive rest service calls to transfer data between html based client side and spring mvc based server side. I’ve added CharacterEncodingFilter to the web.xml as following:
<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>

The problem

When I create a file with name contains Chinese characters, the returned file name is scrambled. The problem is my application uses @ResponseBody in my restful ajax calls to produce json data but those data is not returned in UTF 8 format.
I’ve spent sometime researching and testing a few solutions and I want to share those with you.

Solution 1

Don’t use @ResponseBody. Return void in the controller, and use HtppServletResponse to return string with valid formatting.
@RequestMapping(value = "/rest/create/document")
public void create(Document document, HttpServletRespone respone) throws UnsupportedEncodingException {
 
    Document newDocument = DocumentService.create(Document);
 
    String json = jsonSerializer.serialize(newDocument);
 
    OutputStream outputStream = respones.getOutputStream();
 
    outputStream.write(json.getBytes("UTF-8"));
 
    outputStream.close();
}
Advantage: This will always output text with correct text format, and it’s very simple to understand.
Disadvantage: Very cumbersome. You are going away from the Spring MVC model and writing directly to the HttpServlet model.

Solution 2

Return the an Response Entity object and set it’s content type.
@RequestMapping(value = "/rest/create/document")
public ResponseEntity&lt;String&gt; create(Document document, HttpServletRespone respone) 
 
   HttpHeaders responseHeaders = new HttpHeaders();
 
 responseHeaders.add("Content-Type", "text/html; charset=utf-8");
 
 Document newDocument = DocumentService.create(Document);
 
 String json = jsonSerializer.serialize(newDocument);
 
 return new ResponseEntity&lt;String&gt;(json, responseHeaders, HttpStatus.OK);
}
Better then the last solution, but you still feel there is a lot of plumbing involved.

Solution 3

This is it; this is the most elegant solution and it took me a while to figure out.
@RequestMapping(value = "/rest/create/document", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String create(Document document, HttpServletRespone respone) throws UnsupportedEncodingException {
 
    Document newDocument = DocumentService.create(Document);
 
    return jsonSerializer.serialize(newDocument);
}
When I saw this solution, it blown my mind. Yes this is really that simple. There is no plumbing, you tell spring mvc directly how to return formatted text. The downside is you will always need to return UTF 8 text.

1 comment:

  1. thank you so much ! the solution 3 that what i need, that is really useful but simple! thank author again!

    ReplyDelete