Spring Controllers as JSON API

I am currently working on a couple of projects which are single page web applications, where the clients interacts with spring controllers using JSON. Also, in one project we have a wordpress plugin which interacts with our controllers from wordpress sites.

Thought to summarize what all I needed to get this working.

First, let’s look at an example controller method

@RequestMapping(value = "/{userId}/update",
                method = RequestMethod.POST,
                produces = "application/json")
@ResponseBody
public Object update(@PathVariable("userId") String userId,
                     @Valid MyUserForm userDataForm,
                     BindingResult errors) {
    return userService.updateUser(userId, userDataForm, errors);
}

The object returned by the method is converted to JSON by Jackson object mapper. For this to work, you have to have an ObjectMapper in your application context. To have it, first include it in your project. That means having this in your pom file, assuming you are using maven:

<dependency>
    <artifactId>jackson-mapper-asl</artifactId>
    <groupId>org.codehaus.jackson</groupId>
    <version>1.9.2</version> <!-- or latest version -->
</dependency>

Then, create an object in your application context. If you are using Java configuration, this should do:

@Bean
public ObjectMapper objectMapper()
{
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper;
}

Instead of simlple form fields, request-params or path-variables, sometimes you might need to receive complex JSON data from the client. For that, the client code would look like

        /* this example uses jQuery */
        $.ajax({
            type: "POST",
            url: "url of your controller",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            data:$.toJSON({ /* needs jquery-json (see https://code.google.com/p/jquery-json/) */
                field1: a complex javascript object,
                field2: another complex javascript object
                .
                .
                .
            }),
            success: function(blocks) {
                .
                .
                .
            }
        });

and the controller code like this:

@RequestMapping(value = "/", method = RequestMethod.POST, produces = "application/json")
@ResponseBody
public Object receiveJSON(@RequestBody SomeClass jsonData) {
	return service.process(jsonData);
}

You have to define a SomeClass with fields corresponding to the structure of the data being received.

Finally, if you are switching between http and https protocols, or using your JSON API from a different place, as we do from our wordpress plugin, you will have to circumvent the Javascript Same Origin Policy. For that, I use CORS, or the cors-filter to be specific. To use that, include the following in your pom:

<repository>
   <id>alfresco-for-CORS-Filter</id>
   <url>https://artifacts.alfresco.com/nexus/content/repositories/thirdparty</url>
</repository>
    .
    .
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>1.5</version> <!-- or the latest version -->
        <exclusions>
           <exclusion>
              <groupId>javax.servlet</groupId>
	      <artifactId>servlet-api</artifactId>
           </exclusion>
        </exclusions>
</dependency>           

And, have this in your web.xml:

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
    <init-param>
      <!-- The value of Access-Control-Request-Header should come here for
           OPTIONS HTTP Method to work -->
      <param-name>cors.supportedHeaders</param-name>
      <param-value>origin, content-type, accept</param-value>
    </init-param>		
</filter>
<filter-mapping>
    <filter-name>CORS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

At the client side, my ajax setup, called before any ajax call is made, looks like this:

    /* this example uses javascript */
    $.ajaxSetup({
        cache: false,
        error: function(jqXHR, textStatus, errorThrown) {
            my_function_for_displaying_message("Ajax error while fetching data from server: " + textStatus);
        },
        xhrFields: {
            withCredentials: true
        },
        crossDomain: true,
        dataType: 'json',
        beforeSend:function() {
            my_function_for_displaying_wait();
        },
        complete:function() {
            my_function_for_stopping_displaying_wait();
        }
    });

Please let me know your comments! Hope this would be useful as a quick reference to some (including me) in future.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>