Spring MVC – Easy REST-Based JSON Services with @ResponseBody

Spring 3 makes JSON REST services really easy. This tutorial will show you how in just a few steps.

You can grab the code on GitHub.

Prerequisites
You should have a working Spring MVC Application. If you do not already have a working Spring MVC application set up, follow this tutorial.

We will define three REST services: 1) to retrieve a random Person, 2) to retrieve a Person by ID, and 3) to save a new Person. The services will be consumed using jQuery on a sample page we will set up.

First, I will show the Spring Controller for our REST services, and then we will walk through how they work:

PersonController.java
package com.codetutr.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.codetutr.domain.Person;
import com.codetutr.service.PersonService;

@Controller
@RequestMapping("api")
public class PersonController {
    
    PersonService personService;
    
    @Autowired
    public PersonController(PersonService personService) {
        this.personService = personService;
    }

    @RequestMapping("person/random")
    @ResponseBody
    public Person randomPerson() {
        return personService.getRandom();
    }

    @RequestMapping("person/{id}")
    @ResponseBody
    public Person getById(@PathVariable Long id) {
        return personService.getById(id);
    }
    
    // same as above method, just showing different URL mapping
    @RequestMapping(value="person", params="id")
    @ResponseBody
    public Person getByIdFromParam(@RequestParam Long id) {
        return personService.getById(id);
    }
    
    // handles person form submit
    @RequestMapping(value="person", method=RequestMethod.POST)
    @ResponseBody
    public String savePerson(Person person) {
        personService.save(person);
        return "Saved person: " + person.toString();
    }
}

OK, so, as you can see, we have 4 request handlers in this controller. The first method returns a random person. The next two retrieve a person by ID – just two different approaches to the URL mapping. The last method saves a person.

Remember how Spring controllers usually return a type String (to indicate the resulting view name). Instead, here we are using Spring’s @ResponseBody annotation and returning the object that we want to send to the client. The @ResponseBody annotation tells Spring that we will be returning data in the response body rather than rendering a JSP.

When the @ResponseBody annotation is used, Spring will return the data in a format that is acceptable to the client. That is, if the client request has a header to accept json and Jackson-Mapper is present in the classpath, then Spring will try to serialize the return value to JSON. If the request header indicates XML as acceptable (accept=application/xml) and Jaxb is in the classpath and the return type is annotated with Jaxb annotation, Spring will try to marshall the return value to XML.

As I mentioned, if you want your services to return JSON, you have to have Jackson in the classpath. Here is the only dependency you need to add to your project:

Gradle
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.12'

Or, if you’re using Maven:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.12</version>
</dependency>

Alternatively, if you want your services to return XML, include your favorite Jaxb implementation, eg. com.sun.xml.bind:jaxb:2.1.9.

In a minute, we’ll build a front end to call these services using AJAX, but if you deploy your application now, you can try out your services using a REST client (or just typing the URL into your browser). Eg:

random-person

You can stop following along if you’re content with that. I will just connect all the pieces now by coding the client-side jQuery:

home.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE HTML>
<html>
  <head>
    <title>Spring MVC - Ajax</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <style>
      body { background-color: #eee; font: helvetica; }
      #container { width: 500px; background-color: #fff; margin: 30px auto; padding: 30px; border-radius: 5px; box-shadow: 5px; }
      .green { font-weight: bold; color: green; }
      .message { margin-bottom: 10px; }
      label { width:70px; display:inline-block;}
      .hide { display: none; }
      .error { color: red; font-size: 0.8em; }
    </style>
  </head>
  <body>
  
  <div id="container">
  
    <h1>Person Page</h1>
    <p>This page demonstrates Spring MVC's powerful Ajax functionality. Retrieve a
    random person, retrieve a person by ID, or save a new person, all without page reload.
    </p>
    
    <h2>Random Person Generator</h2>
    <input type="submit" id="randomPerson" value="Get Random Person" /><br/><br/>
    <div id="personResponse"> </div>
    
    <hr/>
    
    <h2>Get By ID</h2>
    <form id="idForm">
      <div class="error hide" id="idError">Please enter a valid ID in range 0-3</div>
      <label for="personId">ID (0-3): </label><input name="id" id="personId" value="0" type="number" />
      <input type="submit" value="Get Person By ID" /> <br /><br/>
      <div id="personIdResponse"> </div>
    </form>
    
    <hr/>
    
    <h2>Submit new Person</h2>
    <form id="newPersonForm">
      <label for="nameInput">Name: </label>
      <input type="text" name="name" id="nameInput" />
      <br/>
      
      <label for="ageInput">Age: </label>
      <input type="text" name="age" id="ageInput" />
      <br/>
      <input type="submit" value="Save Person" /><br/><br/>
      <div id="personFormResponse" class="green"> </div>
    </form>
  </div>
  
  
  <script type="text/javascript">
  
    $(document).ready(function() {
      
      // Random Person AJAX Request
      $('#randomPerson').click(function() {
        $.getJSON('${pageContext.request.contextPath}/api/person/random', function(person) {
          $('#personResponse').text(person.name + ', age ' + person.age);
        });
      });
      
      // Request Person by ID AJAX
      $('#idForm').submit(function(e) {
        var personId = +$('#personId').val();
        if(!validatePersonId(personId)) 
          return false;
        $.get('${pageContext.request.contextPath}/api/person/' + personId, function(person) {
          $('#personIdResponse').text(person.name + ', age ' + person.age);
        });
        e.preventDefault(); // prevent actual form submit
      });
      
      // Save Person AJAX Form Submit
      $('#newPersonForm').submit(function(e) {
        // will pass the form data using the jQuery serialize function
        $.post('${pageContext.request.contextPath}/api/person', $(this).serialize(), function(response) {
          $('#personFormResponse').text(response);
        });
        
        e.preventDefault(); // prevent actual form submit and page reload
      });
      
    });
    
    function validatePersonId(personId) {
      console.log(personId);
      if(personId === undefined || personId < 0 || personId > 3) {
        $('#idError').show();
        return false;
      }
      else {
        $('#idError').hide();
        return true;
      }
    }
    
  
  </script>
  
  </body>
</html>

Once you have everything in place, you should have a page that looks like this:

Spring MVC jQuery Ajax Example

That’s all. Did I miss anything? Let me know in the comments.

Full Source: ZIPGitHub
To run the code from this tutorial: Must have Gradle installed. Clone the GitHub repo or download the ZIP and extract. Open command prompt to code location. Run gradle jettyRunWar. Navigate in browser to http://localhost:8080.

References::
SpringSource Blog – Spring MVC Ajax Simplifications
SpringSource Blog – Spring MVC Enhancements


Tagged with: , , , ,
Posted in Spring
  • Sravan

    I tried the above example but I have jquery date picker in my form. When trying to post the request I get an error “NetworkError: 400 Bad Request”. Any clue on this?

    • Steve Hanson

      Can you use your browsers debugging tools to view the data that is being sent in the POST request and post that here? It sounds like you might be sending malformed data to the server. To debug the request in Chrome: open dev tools (ctrl+shift+i or cmd+optiion+i on mac), then select network pane. You should be able to find the AJAX request there and view the headers.

  • Sebastian Bologescu

    I am getting started with Spring MVC and your tutorials are very useful.
    However, when I try to run this example, the autowiring for PersonService fails. Do I need to annotate PersonService class somehow? I am using the java configuration.

    Thank you for your help!
    Seb

    • Steve Hanson

      Hi Sebastian. Glad the tutorials are useful to you! Sorry for the late response. You should have PersonServiceImpl, which is annotated with @Service. Spring should register this bean automatically if you have @ComponentScan(basePackages=”com.codetutr”) in your WebConfig class. I’d be happy to help you troubleshoot some more. Can you provide more info on the error you are seeing?

  • Mathias Lüstraeten

    I wanted to test the REST-API with curl. I tried

    curl -X POST -H “Content-Type: application/json” -d ‘{“name”:”johnny”,”age”:32}’ http://localhost:8080/api/person

    but only null values are passed:
    Saved person: Person [name=null, age=null]

    Any clue?

    • Steve Hanson

      The Spring controller we set up only accepts request parameters rather than JSON in the body. For example, this curl will work: curl -d “name=johnny&age=32″ http://localhost:8080/api/person.

      It’s easy to change it though to accept JSON in the body to have fully JSON services. Just add “@RequestBody” to your “savePerson” method in PersonController, so it looks like this: savePerson(@RequestBody Person person). @RequestBody tells Spring to extract the data from the body of the request rather than as request params.

      After making that change, I verified that the following works: curl -v -H “Content-Type: application/json” -X POST -d ‘{“name”:”Johnny”,”age”:32}’ http://localhost:8080/api/person.

      Hope that helps!

  • http://geffchang.wordpress.com/ Geff Chang

    Random Person AJAX Request and Save Person AJAX Form Submit perform the same operations. They’re acting on #randomPerson. Duplicate?

    • Steve Hanson

      You are correct. Fixed now. Thanks for the feedback!

  • ag

    great tutorial but how can I run this on tomcat instead of jetty?

    • Steve Hanson

      Run “Gradle eclipse” from the command line. This will generate Eclipse project files. Then you can “Import existing project” into Eclipse and run on any server you have set up in eclipse. Alternatively, you could run “gradle war” to build a war file and then manually drop it into a server you have set up. Let me know if you have any more questions about this.

  • Jan Akerman

    Hi Steven, I’ve been following along with your tutorial but unfortunately I’m a bit stuck.

    I’m getting a Error 406 NOT_ACCEPTABLE when I send a request to /api/person/random. From reading around, it seems as though I may not have jackson set up properly but as far as I can tell I’ve got my dependancies set up correctly:


    compile ‘org.codehaus.jackson:jackson-mapper-asl:1.9.12′

    As long as I have this dependancy set up, shouldn’t Spring’s MappingJackson2HttpMessageConverter automatically convert the response to JSON?

    If you have any advice I’d really appreciate it!

    Thanks,
    Jan

    • Jan Akerman

      Took me about four hours, but I finally worked out it was because I hadn’t provided getters in my Person class. I’m assuming that because jackson couldn’t get at the private variables, it wasn’t returning json & was somehow breaking things.

      Jan

      • Steve Hanson

        Glad to see you resolved it, and thanks for following up with your solution! Sorry I didn’t get back to you earlier today – though I’m not sure I would have been much help! Cheers.

        steve

        • Pranav Sharma

          Hey Steve, Seems I am doing something wrong. I imported this project using Gradle to eclipse. Installed it successfully to Tomcat. But while running url http://localhost:8080/api/person/random/
          I am geting 404. I am baffled.

  • Abhishek Amte

    This is a great tutorial. Thanks alot. I am new to Spring so this question may seem silly, how can i send (submit from a form) a complex object, say a Person object having an Address object within it, to the controller

  • vikas

    is der any possibilities to used maven…….can u provide pom.xml changes

  • Michal Kolísko Kolesár

    Hi all. I get error Content type ‘application/json’ not supported. Could you tell me what is wrong please? Thank you

    ——- JAVA ——

    private String toJson(Object o) {
    Gson gson = new Gson();
    return gson.toJson(o);
    }

    @RequestMapping(value=”login”, method=RequestMethod.POST)
    @RemotingExclude
    @ResponseBody
    public User loginJSON(@RequestBody User user, @RequestBody String type) throws Exception {
    return login(user, type);
    }

    ———-SHELL——

    $ curl -X POST -H “Content-Type: application/json” -d ‘{{“email”:”aa@bb.cz”,”password”:”aa”},”type”:”MOBILE_TYPE”}’ http://localhost:8080/hotmusic-war/sample/profile/login

    ——- LOG —–

    19:22:37,203 DEBUG DispatcherServlet:640 – Bound request context to thread: org.apache.catalina.connector.RequestFacade@882100

    19:22:37,203 DEBUG DispatcherServlet:693 – DispatcherServlet with name ‘sample’ processing POST request for [/hotmusic-war/sample/profile/login]

    19:22:37,203 DEBUG DispatcherServlet:927 – Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@d9dbd1] in DispatcherServlet with name ‘sample’

    19:22:37,203 DEBUG DefaultAnnotationHandlerMapping:221 – Mapping [/profile/login] to HandlerExecutionChain with handler [cz.hotmusic.service.impl.ProfileService@16f2b7f] and 2 interceptors

    19:22:37,203 DEBUG DispatcherServlet:961 – Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@190d0d2]

    19:22:37,203 DEBUG AnnotationMethodHandlerExceptionResolver:132 – Resolving exception from handler [cz.hotmusic.service.impl.ProfileService@16f2b7f]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json’ not supported

    19:22:37,218 DEBUG ResponseStatusExceptionResolver:132 – Resolving exception from handler [cz.hotmusic.service.impl.ProfileService@16f2b7f]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json’ not supported

    19:22:37,218 DEBUG DefaultHandlerExceptionResolver:132 – Resolving exception from handler [cz.hotmusic.service.impl.ProfileService@16f2b7f]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/json’ not supported

    19:22:37,218 DEBUG DispatcherServlet:824 – Null ModelAndView returned to DispatcherServlet with name ‘sample’: assuming HandlerAdapter completed request handling

    19:22:37,218 DEBUG DispatcherServlet:667 – Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@882100

    19:22:37,218 DEBUG DispatcherServlet:674 – Successfully completed request

    19:22:37,218 DEBUG XmlWebApplicationContext:301 – Publishing event in WebApplicationContext for namespace ‘sample-servlet’: ServletRequestHandledEvent: url=[/hotmusic-war/sample/profile/login]; client=[127.0.0.1]; method=[POST]; servlet=[sample]; session=[null]; user=[null]; time=[15ms]; status=[OK]

    19:22:37,218 DEBUG XmlWebApplicationContext:301 – Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/hotmusic-war/sample/profile/login]; client=[127.0.0.1]; method=[POST]; servlet=[sample]; session=[null]; user=[null]; time=[15ms]; status=[OK]

  • Noman Sadiq

    Hi nice post. Can you please tell me is it really necessary to implement spring MVC to implement spring REST interface? I already have spring+EJB3.0 web service interface but now i want to implement restful service using spring but in all the Google results they mentioned spring based web project. Let me know if there any other way to implement spring REST without adding spring web capabilities into the existing maven based project.
    Thanks

  • Majid Lotfi

    Hi,
    Thank you for this tutorial.
    Is there any maven version of this tutorial ?

  • Majid Lotfi

    HI,

    I just added this line :compile ‘org.codehaus.jackson:jackson-mapper-asl:1.9.12′

    to build.gradle , it did not add the jackson jar in the library, is there any command to do for this ?
    Thanks

  • Aurélien Leboulanger

    is it possible to define specific serialization/deserialization of the POJO object, via annotations maybe? for example define rules for not serialize a field if the value is empty

  • Majid Lotfi

    Hi,

    If you try to keep clicking in the random button “Get Random Person”, it will keep giving the same person returned, is this from server-side (spring) or jquery ?

    Thanks.

  • Deme Carv

    Steve, congratulations for this excelent and practical tutorial. How would you deal with a model that has a timestamp variable? Let’s imagine that you already have the model and it has the born date as java.sql.timestamp and you can’t change it. How would you deal with? I have a model with timestamp and I tried to call from the client that way but it doesn’t work. It only works if I send the date as String and have the variable in the model as String.

    HttpPost postRequest = new HttpPost(“http://localhost:8080/my_app/similiar_to_person”);
    ObjectMapper mapper = new ObjectMapper();
    String json = “”;
    Map map = new HashMap();
    map.put(“subCd”, “A”);
    SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy HH:mm:ss”);
    map.put(“firstDT”, “/Date(” + format.format(new Date()) + “)/”);
    json = mapper.writeValueAsString(map);
    StringEntity input = new StringEntity(json);
    input.setContentType(“application/json”);
    postRequest.setEntity(input);
    HttpResponse response = httpClient.execute(postRequest);

Sponsored
Follow Me
RSS FeedFollow me on Twitter
Categories