Close

Spring MVC - HTTP Message Body handling using @RequestBody and @ResponseBody

[Last Updated: Sep 2, 2017]

Spring provides a generic mechanism of converting HTTP Message body to/from Java objects.

Based on 'Content-Type' and 'Accept' of request header values, a handler method is first mapped.

If the handler method has @RequestBody in it's parameter list and/or @ResponseBody as it's return type, then a suitable implementation of HttpMessageConverter is selected depending on media type of request or response.

What is HttpMessageConverter?

HttpMessageConverter is a strategy interface that can convert HTTP request body to an object type or the an object type to HTTP response body.

By default Spring supports various HttpMessageConverter and more can be discovered if they are in class path. In this tutorial we are going to show examples of the followings:

  • ByteArrayHttpMessageConverter converts byte arrays.
  • StringHttpMessageConverter converts strings.
  • FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>


Byte Array and String conversions

@Controller
@RequestMapping("message")
public class MyController {

    @RequestMapping(consumes = MediaType.TEXT_PLAIN_VALUE,
                    produces = MediaType.TEXT_HTML_VALUE,
                    method = RequestMethod.GET)
    @ResponseBody
    public String handleRequest (@RequestBody byte[] bytes, @RequestBody String str) {

        System.out.println("body in bytes: "+bytes);
        System.out.println("body in string: "+str);

        return "<html><body><h1>Hi there</h1></body></html>";
    }
}

Let's test above controller with JUnit test. (more about Spring MVC JUnit tests)

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup () {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testController () throws Exception {
        ResultMatcher ok = MockMvcResultMatchers.status()
                                                .isOk();

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/message")
                                                        .contentType(MediaType.TEXT_PLAIN)
                                                        .content("test message");
        this.mockMvc.perform(builder)
                    .andExpect(ok)
                    .andDo(MockMvcResultHandlers.print());

    }
}

Output:

body in bytes: [B@20b2475a
body in string: test message

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /message
       Parameters = {}
          Headers = {Content-Type=[text/plain]}

Handler:
             Type = com.logicbig.example.MyController
           Method = public java.lang.String com.logicbig.example.MyController.handleRequest(byte[],java.lang.String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[text/html], Content-Length=[43]}
     Content type = text/html
             Body = <html><body><h1>Hi there</h1></body></html>
    Forwarded URL = null
   Redirected URL = null
          Cookies = []



Form POST data conversion

The content-type of form data is application/x-www-form-urlencoded:

@Controller
@RequestMapping("message")
public class MyController {

    @RequestMapping(method = RequestMethod.POST,
                    consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public void handleFormRequest(@RequestBody MultiValueMap<String, String> formParams) {
    System.out.println("form params received " + formParams);
    }
}


What is @ResponseStatus?

Marks a method or exception class with the status code() and reason() that should be returned. The status code is applied to the HTTP response when the handler method is invoked and overrides status information set by other means, like ResponseEntity or "redirect:".

The Unit test:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup () {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testFormPostController () throws Exception {
        ResultMatcher ok = MockMvcResultMatchers.status()
                                                .isOk();

        MockHttpServletRequestBuilder builder =
                   MockMvcRequestBuilders.post("/message")
                                         .contentType("application/x-www-form-urlencoded")
                                         //use param instead of content
                                         .param("name", "joe");

        this.mockMvc.perform(builder)
                    .andExpect(ok);
    }
}

Output:

form params received {name=[joe]}


Form PUT data conversion

@Controller
@RequestMapping("message")
public class MyController {

   @RequestMapping(method = RequestMethod.PUT,
                    consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
   @ResponseStatus(HttpStatus.CREATED)
   public void handleFormPutRequest(@RequestBody MultiValueMap<String, String> formParams){
        System.out.println("form params received " + formParams);
   }
}

The JUnit test:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup () {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testFormPutController () throws Exception {
        ResultMatcher created = MockMvcResultMatchers.status()
                                                .isCreated();

        MockHttpServletRequestBuilder builder =
                            MockMvcRequestBuilders.put("/message")
                                         .contentType("application/x-www-form-urlencoded")
                                         .content("name=mike");
        this.mockMvc.perform(builder)
                    .andExpect(created);


    }
}

Output:

form params received {name=[mike]}



What's Next?

In next tutorials we are going to demonstrate how to use @RequestBody and @ResponseBody to convert request/response body data to backing objects. The commonly used data types are XML and JSON. We can always write our own HttpMessageConverter for other data types. Also note that any data type can always be mapped to String or byte[].

FormHttpMessageConverter (which is used for content type application/x-www-form-urlencoded) cannot bind to backing Objects using @RequestBody and @ResponseBody. In this case we should always use either Spring implicit DataBinder approach or @ModelAttribute. If we still want to use @RequestBody or @ResponseBody then we should use MultiValueMap (as we showed above) instead of a backing object.

Example Project

Dependencies and Technologies Used:

  • Spring Web MVC 4.2.4.RELEASE: Spring Web MVC.
  • Spring TestContext Framework 4.2.4.RELEASE: Spring TestContext Framework.
  • Java Servlet API 3.0.1
  • JUnit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • JDK 1.8
  • Maven 3.0.4

Spring Message Body Select All Download
  • spring-message-body-conversion
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyController.java
        • test
          • java
            • com
              • logicbig
                • example

    See Also