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.
HTTP Message Body handling using @RequestBody and @ResponseBody
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
package com.logicbig.example;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("message")
public class MyController {
@GetMapping(consumes = MediaType.TEXT_PLAIN_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String handleRequest(@RequestBody byte[] bytes) {
//We can replace byte[] with String above
System.out.println("body in bytes: " + bytes);
System.out.println("body in string: " + new String(bytes));
return "Hi there";
}
.............
}
Let's test above controller with JUnit test. (more about Spring MVC JUnit tests)
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvcTester mockMvc;
@BeforeEach
public void setup() {
this.mockMvc = MockMvcTester.from(this.wac);
}
@Test
public void testController() {
assertThat(mockMvc.get().uri("/message")
.contentType(MediaType.TEXT_PLAIN)
.content("test message")
.exchange())
.hasStatusOk()
.hasBodyTextEqualTo("Hi there");
}
.............
}
mvn clean test -Dtest="RegistrationControllerTest#testController" Output$ mvn clean test -Dtest="RegistrationControllerTest#testController" [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.logicbig.example:spring-message-body-conversion:war:1.0-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 48, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] --------< com.logicbig.example:spring-message-body-conversion >--------- [INFO] Building spring-message-body-conversion 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-message-body-conversion --- [INFO] Deleting D:\example-projects\spring-mvc\spring-message-body-conversion\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\main\resources [INFO] [INFO] --- compiler:3.5.1:compile (default-compile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\spring-message-body-conversion\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\test\resources [INFO] [INFO] --- compiler:3.5.1:testCompile (default-testCompile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\example-projects\spring-mvc\spring-message-body-conversion\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-message-body-conversion --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.RegistrationControllerTest body in bytes: [B@2b9ed6da body in string: test message [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.591 s -- in com.logicbig.example.RegistrationControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.768 s [INFO] Finished at: 2026-06-22T10:57:05+08:00 [INFO] ------------------------------------------------------------------------
Form POST data conversion
The content-type of form data is application/x-www-form-urlencoded:
package com.logicbig.example;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("message")
public class MyController {
.............
@RequestMapping(method = RequestMethod.POST, consumes = MediaType
.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public String handleFormRequest(@RequestBody
MultiValueMap<String, String> formParams) {
System.out.println("form params received " + formParams);
return "name: " + formParams.get("name").get(0);
}
.............
}
HTTP Message Body handling using @RequestBody and @ResponseBody
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:
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvcTester mockMvc;
@BeforeEach
public void setup() {
this.mockMvc = MockMvcTester.from(this.wac);
}
.............
@Test
public void testFormPostController() {
assertThat(mockMvc.post().uri("/message")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.accept(MediaType.TEXT_PLAIN)
.param("name", "joe")
.exchange())
.hasStatusOk()
.hasBodyTextEqualTo("name: joe");
}
.............
}
mvn clean test -Dtest="RegistrationControllerTest#testFormPostController" Output$ mvn clean test -Dtest="RegistrationControllerTest#testFormPostController" [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.logicbig.example:spring-message-body-conversion:war:1.0-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 48, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] --------< com.logicbig.example:spring-message-body-conversion >--------- [INFO] Building spring-message-body-conversion 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-message-body-conversion --- [INFO] Deleting D:\example-projects\spring-mvc\spring-message-body-conversion\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\main\resources [INFO] [INFO] --- compiler:3.5.1:compile (default-compile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\spring-message-body-conversion\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\test\resources [INFO] [INFO] --- compiler:3.5.1:testCompile (default-testCompile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\example-projects\spring-mvc\spring-message-body-conversion\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-message-body-conversion --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.RegistrationControllerTest form params received {name=[joe]} [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.562 s -- in com.logicbig.example.RegistrationControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.740 s [INFO] Finished at: 2026-06-22T16:22:31+08:00 [INFO] ------------------------------------------------------------------------
Form PUT data conversion
package com.logicbig.example;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("message")
public class MyController {
.............
@RequestMapping(method = RequestMethod.PUT,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
public String handleFormPutRequest(@RequestBody
MultiValueMap<String, String> formParams) {
System.out.println("form params received " + formParams);
return "name: "+formParams.get("name").get(0);
}
}
The JUnit test:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class RegistrationControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvcTester mockMvc;
@BeforeEach
public void setup() {
this.mockMvc = MockMvcTester.from(this.wac);
}
.............
@Test
public void testPutController() {
assertThat(mockMvc.put().uri("/message")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.accept(MediaType.TEXT_PLAIN)
.content("name=mike")
.exchange())
.hasStatus(HttpStatus.CREATED)
.hasBodyTextEqualTo("name: mike");
}
}
mvn clean test -Dtest="RegistrationControllerTest#testPutController" Output$ mvn clean test -Dtest="RegistrationControllerTest#testPutController" [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.logicbig.example:spring-message-body-conversion:war:1.0-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-war-plugin is missing. @ line 48, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] --------< com.logicbig.example:spring-message-body-conversion >--------- [INFO] Building spring-message-body-conversion 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-message-body-conversion --- [INFO] Deleting D:\example-projects\spring-mvc\spring-message-body-conversion\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\main\resources [INFO] [INFO] --- compiler:3.5.1:compile (default-compile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\spring-message-body-conversion\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-message-body-conversion --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-message-body-conversion\src\test\resources [INFO] [INFO] --- compiler:3.5.1:testCompile (default-testCompile) @ spring-message-body-conversion --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\example-projects\spring-mvc\spring-message-body-conversion\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-message-body-conversion --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.RegistrationControllerTest form params received {name=[mike]} [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.615 s -- in com.logicbig.example.RegistrationControllerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.864 s [INFO] Finished at: 2026-06-22T18:11:42+08:00 [INFO] ------------------------------------------------------------------------
What's Next?
In the 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 ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.6 (Spring TestContext Framework)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
- JDK 25
- Maven 3.9.11
|