Why @RequestBody Is More Than Just JSON: Handling Multiple Request Formats in Spring Boot
This article explains how Spring MVC’s @RequestBody annotation is a universal entry point for HTTP request bodies, supporting JSON, XML, plain text, binary streams, maps, lists, and custom media types through its HttpMessageConverter mechanism, with concrete code examples and configuration details.
Why @RequestBody supports many formats
Spring MVC processes the request body through the HttpMessageConverter mechanism. The framework selects a converter based on the request Content‑Type , the target method‑parameter type, and the converters that are registered in the application context.
application/json → MappingJackson2HttpMessageConverter application/xml → MappingJackson2XmlHttpMessageConverter text/plain → StringHttpMessageConverter application/octet-stream → ByteArrayHttpMessageConverter The power of @RequestBody therefore comes from the message‑conversion infrastructure, not from the annotation itself.
Receiving JSON (the most common case)
package com.icoderoad.controller;
import com.icoderoad.dto.UserDTO;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/create")
public String create(@RequestBody UserDTO userDTO) {
return "用户名:" + userDTO.getUsername();
}
} package com.icoderoad.dto;
public class UserDTO {
private String username;
private Integer age;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
} POST /user/create
Content-Type: application/json
{
"username": "icoderoad",
"age": 18
}The request is handled by MappingJackson2HttpMessageConverter, which deserialises JSON into UserDTO.
Receiving XML
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency> package com.icoderoad.dto;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "user")
public class XmlUserDTO {
private String username;
private Integer age;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
} package com.icoderoad.controller;
import com.icoderoad.dto.XmlUserDTO;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/xml")
public class XmlController {
@PostMapping(value = "/receive", consumes = "application/xml")
public String receiveXml(@RequestBody XmlUserDTO dto) {
return dto.getUsername();
}
} <user>
<username>icoderoad</username>
<age>20</age>
</user>Receiving plain text (common in webhook callbacks)
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/text")
public class TextController {
@PostMapping(value = "/receive", consumes = "text/plain")
public String receive(@RequestBody String body) {
System.out.println(body);
return "success";
}
} Content-Type: text/plain
hello spring bootThe underlying converter is StringHttpMessageConverter.
Receiving binary data (byte[])
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/binary")
public class BinaryController {
@PostMapping(value = "/upload", consumes = "application/octet-stream")
public String upload(@RequestBody byte[] data) {
System.out.println("数据长度:" + data.length);
return "ok";
}
}The request is processed by ByteArrayHttpMessageConverter. This is suitable for protobuf, image streams, audio streams, or any custom binary protocol.
Receiving dynamic fields with Map
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/map")
public class MapController {
@PostMapping("/receive")
public String receive(@RequestBody Map<String, Object> body) {
System.out.println(body);
return "ok";
}
} {
"username": "icoderoad",
"role": "admin",
"score": 99
}Receiving a List of objects (batch operations)
package com.icoderoad.controller;
import com.icoderoad.dto.UserDTO;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/batch")
public class BatchController {
@PostMapping("/save")
public String save(@RequestBody List<UserDTO> list) {
return "数据量:" + list.size();
}
} [
{"username": "tom", "age": 18},
{"username": "jack", "age": 20}
]Streaming large files with InputStream
Using @RequestBody byte[] loads the entire payload into memory, which can cause OOM for very large files. Reading the raw InputStream from the servlet request avoids this.
package com.icoderoad.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
import java.io.InputStream;
@RestController
@RequestMapping("/stream")
public class StreamController {
@PostMapping("/upload")
public String upload(HttpServletRequest request) throws Exception {
InputStream inputStream = request.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
System.out.println("读取字节数:" + len);
}
return "success";
}
}How Spring Boot auto‑detects these formats
Spring Boot’s HttpMessageConvertersAutoConfiguration scans the classpath for libraries such as Jackson, XML support, Gson, or Kotlin Serialization and registers the corresponding converters dynamically. Adding a single dependency like jackson-dataformat-xml instantly enables XML handling.
Custom HttpMessageConverter – advanced use case
When a system requires a proprietary protocol, protobuf, encrypted messages, or a private media type, a custom HttpMessageConverter can be implemented.
package com.icoderoad.converter;
import org.springframework.http.*;
import org.springframework.http.converter.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class CustomMessageConverter extends AbstractHttpMessageConverter<String> {
public CustomMessageConverter() {
super(new MediaType("application", "x-icoderoad"));
}
@Override
protected boolean supports(Class<?> clazz) {
return String.class == clazz;
}
@Override
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
return new String(inputMessage.getBody().readAllBytes());
}
@Override
protected void writeInternal(String s, HttpOutputMessage outputMessage) throws IOException {
outputMessage.getBody().write(s.getBytes());
}
}Requests with Content-Type: application/x-icoderoad are automatically handled by CustomMessageConverter.
Typical scenarios where non‑JSON formats appear
Third‑party payment callbacks: XML, text/plain, form‑urlencoded
IoT device communication: application/octet-stream
Log collection platforms: plain text streams
AI / audio‑video systems: audio/video streams, protobuf, binary data
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
