1. Overview

In this tutorial, we'll look at the @RequestBody and @ResponseBody annotations in Spring MVC.

2. Sample Application

Throughout the tutorial, we'll build a REST service that provides several operations for the Person entities:

public class Person {

    private String id;
    private String firstName;
    private String lastName;
    private int age;

    // Getters and setters...
}

The Person class is a simple Java object with some data fields.

3. Using @RequestBody

The @RequestBody annotation tells Spring MVC to read the request body and assign the value to the annotated method parameter.

As the first step, we'll create an endpoint:

@RestController
public class PersonController {

    @PostMapping(value = "/person", consumes = MediaType.APPLICATION_JSON_VALUE)
    public void savePerson(@RequestBody Person person) {
        // Save person...
    }
}

Here, we've created PersonController and added a POST endpoint to save the incoming Person data. Since @RequestBody is put before the person parameter, Spring MVC tries to convert the request body to a Person object. Moreover, our endpoint expects JSON payload because we're specifying the consumes attribute of @PostMapping as such.

Now that we have our endpoint, we'll perform a POST request against it:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc(printOnlyOnFailure = false)
public class PersonControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldCreatePerson() throws Exception {
        mockMvc.perform(post("/person")
          .contentType(MediaType.APPLICATION_JSON_VALUE)
          .content("{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":30}")
          .accept(MediaType.APPLICATION_JSON_VALUE))
          .andExpect(status().isOk())
          .andExpect(content().string(""));
    }
}

In this test, we're setting the Content-Type request header as application/json and sending a JSON string in the request body.

Finally, when the request hits the application, Spring MVC will perform the actual conversion operation using the HttpMessageConverter implementations. Then the converted object will be assigned to the person parameter in our controller method.

If the application can't find any suitable HttpMessageConverter to read the request body, it will return 415 Unsupported Media Type. Moreover, Spring will log the error:

Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/atom+xml;charset=UTF-8' not supported]

3.1. @RequestBody and Request Properties

We'll now look at some important points related to the usage of @RequestBody.

Firstly, @RequestBody doesn't depend on the media type. In the previous example, we've used JSON, but it is also fine to use XML for data representation.

@PostMapping(value = "/person", consumes = MediaType.APPLICATION_XML_VALUE)
public void savePersonInXML(@RequestBody Person person) {
    // Save person...
}

Here, we're setting our media type expectation as XML - application/xml - in the consumes attribute. As long as there is an HttpMessageConverter that can handle the XML payload, our endpoint will continue operating as before.

Secondly, as the name implies, we can use @RequestBody if the request has an entity or body. The HTTP specification doesn't openly prevent any HTTP method for having a request body, but it is wise to not use the HTTP GET. It is because the HTTP servers and proxy servers may discard the request body for the GET requests and fail in unexpected ways.

4. Using @ResponseBody

@ResponseBody makes Spring MVC write the method return value as the response body. 

There are two options to introduce @ResponseBody to our controller endpoints.

As the first option, we can annotate the parent controller class with @RestController. This way, all endpoint methods will implicitly have @ResponseBody.

@RestController
public class PersonController {

    @GetMapping(value = "/person", produces = MediaType.APPLICATION_JSON_VALUE)
    public Person getPerson(@RequestParam("id") String id) {
        Person foundPerson = queryPerson(id);
        return foundPerson;
    }

    @PostMapping(value = "/person", consumes = MediaType.APPLICATION_JSON_VALUE)
    public void savePerson(@RequestBody Person person) {
        // Save person...
    }

   // Other endpoints...
}

Here, both getPerson and savePerson have the @ResponseBody annotation implicitly.

As the second option, we can annotate a specific controller method explicitly with @ResponseBody:

@Controller
public class PersonController {

    @GetMapping(value = "/person", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Person getPerson(@RequestParam("id") String id) {
        Person foundPerson = queryPerson(id);
        return foundPerson;
    }
}

In this case, we're annotating the getPerson method with @ResponseBody and the parent controller class with @Controller.

As a result, Spring will first take the returned Person object and then search for a suitable HttpMessageConverter. If found, it will use the found HttpMessageConverter instance to write the object to the response.

In the case of no suitable HttpMessageConverter, the application will return 406 Not Acceptable, logging the error:

Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]

5. Summary

In this tutorial, we've looked at the usages of the @RequestBody and @ResponseBody annotations.

As always, the source code for all examples is available on Github.