Spring Boot Request & Response: @RequestBody, @ResponseBody, HttpEntity
Master Spring Boot request/response handling with @RequestBody, @ResponseBody, HttpEntity. Learn serialization, validation, common pitfalls, and security best practices.
Master Spring Boot request/response handling with @RequestBody, @ResponseBody, HttpEntity. Learn serialization, validation, common pitfalls, and security best practices. The guide uses practical examples to explain how http request and response processing works in spring mvc, @requestbody: binding incoming data and shows how to apply the ideas in a Spring Boot project.
Spring Boot Request & Response: @RequestBody, @ResponseBody, HttpEntity
Every REST API endpoint is a conversation about data. The client sends a request, the server responds. Spring Boot handles the plumbing between those two moments through annotations and classes that form the core of your web layer. This post covers @RequestBody, @ResponseBody, HttpEntity, and RequestEntity so you know exactly what each one does and when to use it.
How HTTP Request and Response Processing Works in Spring MVC
Introduction
Request and response handling is the boundary where a Spring Boot application translates HTTP input into application behavior and turns results into reliable API responses. This guide covers binding, validation, headers, status codes, serialization, error responses, and the design choices that keep controller contracts clear.
@RequestBody: Binding Incoming Data
@RequestBody tells Spring to read the entire request body and deserialize it into the method parameter it decorates. The parameter type determines which converter gets used.
Basic Usage
@PostMapping("/users")
public UserResponse createUser(@RequestBody UserRequest request) {
return userService.create(request);
}
Spring reads the request body bytes, deserializes the JSON into a UserRequest POJO, and passes it to your method. No manual parsing involved.
When to Use @RequestBody
Use @RequestBody when you need to accept complex data structures from the request body, particularly JSON or XML payloads. It handles the deserialization automatically and works with Bean Validation.
When NOT to Use @RequestBody
Do not use @RequestBody for simple query parameters or path variables. Use @RequestParam or @PathVariable instead. @RequestBody consumes the entire request body, which means you cannot combine it with other body-based parameters in the same method.
// Avoid this for simple parameters
@PostMapping("/search")
public List<User> search(@RequestBody String query) { // Anti-pattern
// ...
}
// Correct approach for query parameters
@GetMapping("/search")
public List<User> search(@RequestParam String q) {
return userService.search(q);
}
@ResponseBody: Returning Data Directly
@ResponseBody on a controller method tells Spring to serialize the return value into the response body instead of passing it to a view resolver. The return value goes through the HttpMessageConverter pipeline just like @RequestBody, but in reverse.
Returning Data with @ResponseBody
@GetMapping("/users/{id}")
@ResponseBody
public UserResponse getUser(@PathVariable Long id) {
return userService.findById(id);
}
The UserResponse object gets serialized to JSON and written to the HTTP response stream. No view resolution, no templates.
@RestController: The Compound Annotation
If every method in your controller returns data rather than a view, @RestController is cleaner than @Controller plus @ResponseBody on every method. It is a composed annotation combining @Controller and @ResponseBody.
@RestController
@RequestMapping("/api/users")
public class UserController {
// All methods implicitly serialize to response body
}
HttpEntity and RequestEntity: Full Control Over Headers and Body
HttpEntity and RequestEntity give you access to the entire HTTP message, not just the body. HttpEntity exposes both headers and body. RequestEntity extends it with HTTP method and URI information.
Reading the Full Request with HttpEntity
@PostMapping("/webhook")
public ResponseEntity<Void> handleWebhook(HttpEntity<MyEvent> entity) {
HttpHeaders headers = entity.getHeaders();
MyEvent body = entity.getBody();
// Full access to headers and body
return ResponseEntity.ok().build();
}
Building Requests with RequestEntity
RequestEntity is particularly useful when you make outbound HTTP calls with RestTemplate or WebClient and need precise control over the HTTP method, URL, headers, and body as a single object.
RequestEntity<CreateUserRequest> request = RequestEntity
.post("https://api.example.com/users")
.header("Authorization", "Bearer " + token)
.header("X-Custom-Header", "value")
.body(createUserRequest);
ResponseEntity<UserResponse> response = restTemplate.exchange(request, UserResponse.class);
HttpEntity for Responses
ResponseEntity is more common on the response side because it carries status codes too. HttpEntity works fine when you only need to control headers and body without setting a particular status code.
@GetMapping("/download")
public HttpEntity<byte[]> downloadFile(@PathVariable String filename) {
byte[] fileBytes = fileService.loadFile(filename);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.attachment()
.filename(filename)
.build());
return new HttpEntity<>(fileBytes, headers);
}
Request/Response Lifecycle Diagram
The diagram below shows the complete flow from an incoming JSON request to an outgoing JSON response, showing where each Spring component operates.
sequenceDiagram
participant Client
participant DispatcherServlet
participant HandlerAdapter
participant RequestBody
participant Controller
participant ResponseBody
participant HttpMessageConverter
participant Client
Client->>DispatcherServlet: POST /api/users {"name": "Alice"}
DispatcherServlet->>HandlerAdapter: Dispatch to controller
HandlerAdapter->>HttpMessageConverter: Deserialize request body
HttpMessageConverter->>RequestBody: Convert JSON to UserRequest
RequestBody->>Controller: invoke(createUser)
Controller->>Controller: business logic
Controller-->>ResponseBody: return UserResponse
ResponseBody-->>HttpMessageConverter: Serialize to JSON
HttpMessageConverter-->>DispatcherServlet: JSON bytes
DispatcherServlet-->>Client: 200 OK {"id": 1, "name": "Alice"}
Implementation Examples
JSON Request and Response
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@PostMapping
public ProductResponse createProduct(@RequestBody @Valid CreateProductRequest request) {
return productService.create(request);
}
@GetMapping("/{id}")
public ProductResponse getProduct(@PathVariable Long id) {
return productService.findById(id);
}
@PutMapping("/{id}")
public ProductResponse updateProduct(
@PathVariable Long id,
@RequestBody @Valid UpdateProductRequest request) {
return productService.update(id, request);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
productService.delete(id);
return ResponseEntity.noContent().build();
}
}
XML Request and Response
Add the Jackson XML extension to your pom.xml to enable XML support:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Spring Boot auto-registers the JacksonXmlHttpMessageConverter. The same controller handles both JSON and XML based on the Accept and Content-Type headers.
Handling Binary Data
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(
@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("File is empty");
}
String storedPath = fileStorageService.store(file);
return ResponseEntity.ok(storedPath);
}
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
Resource file = fileStorageService.loadAsResource(filename);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"")
.body(file);
}
Validation and Error Handling
@RequestBody parameters are validated by default when you annotate them with @Valid or @Validated. If validation fails, Spring raises a MethodArgumentNotValidException.
@PostMapping("/users")
public UserResponse createUser(
@RequestBody @Valid CreateUserRequest request,
BindingResult result) {
if (result.hasErrors()) {
throw new ValidationException(result.getFieldErrors());
}
return userService.create(request);
}
A better approach uses a centralized exception handler:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
String message = fieldErrors.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_ERROR", message));
}
}
Failure Scenarios
Understanding what can go wrong helps you design more resilient APIs.
JSON Parsing Failures
If the request body contains malformed JSON, Spring throws an HttpMessageNotReadableException. This typically happens when clients send corrupted payloads or use the wrong content type.
// Malformed JSON
{"name": "Alice",
"email": "alice@example.com"
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"status": 400, "error": "Bad Request", "message": "JSON parse error"}
Type Mismatch Errors
When the deserialized JSON does not match the target Java type, Spring throws a TypeMismatchException or HttpMessageNotWritableException depending on the direction.
Empty Request Bodies
Sending an empty body with @RequestBody results in a HttpMessageNotReadableException in most cases, though behavior can vary based on the content type and converter.
Character Encoding Issues
If the request or response contains non-ASCII characters and the encoding is not properly specified, you may see garbled text. Set the Content-Type header with charset specification:
return ResponseEntity.ok()
.contentType(new MediaType("application", "json", StandardCharsets.UTF_8))
.body(response);
When to Use / When NOT to Use
Use @RequestBody When
Use @RequestBody when your endpoint needs to accept a structured payload — JSON, XML, or another format that deserializes into a Java object. It is the right tool for POST, PUT, and PATCH requests that carry data in the request body. Combine it with @Valid to trigger Bean Validation automatically before your method executes.
Avoid @RequestBody When
Do not use @RequestBody for simple scalar values like a search query string, a single ID, or a flag. A query like /search?q=alice should use @RequestParam String q, not @RequestBody String q. Using @RequestBody for simple values consumes the entire request body for a single value and prevents you from also using @RequestParam in the same method.
When to Use @ResponseBody
Use @ResponseBody when you want the return value serialized directly into the response body. If every method in your controller returns data (JSON, XML, plain text) rather than a view, use @RestController instead of annotating every method individually.
When to Use HttpEntity or RequestEntity
Use HttpEntity when you need read or write access to HTTP headers in addition to the body. RequestEntity is particularly useful when building outbound HTTP requests with RestTemplate or WebClient, where you need to specify the HTTP method, URL, headers, and body as a single object. For most controller endpoints, @RequestBody and @ResponseBody are simpler and more idiomatic.
When to Use ResponseEntity
Use ResponseEntity when you need full control over the HTTP response — setting specific status codes, adding custom headers, or deciding the body conditionally based on business logic. It is the right choice for endpoints like DELETE that might return 204 No Content or 404 Not Found depending on whether the resource existed.
Trade-Off Table
| Aspect | @RequestBody | @ResponseBody | HttpEntity / RequestEntity |
|---|---|---|---|
| Header access | No | No | Yes |
| Body serialization | Deserialization only | Serialization only | Both |
| HTTP method awareness | No | No | Yes (RequestEntity) |
| Status code control | No | No | Via ResponseEntity |
| Validation integration | Yes (@Valid) | No | No |
| Typical use case | Accepting JSON/XML payloads | Returning JSON/XML responses | Web clients, file downloads, precise header control |
Security Notes
JSON Injection
Never trust deserialized JSON input without validation. An attacker could inject unexpected fields that your deserialized object might persist or process unsafely. Validate input using Bean Validation annotations:
public class CreateUserRequest {
@NotBlank
@Size(min = 2, max = 50)
private String name;
@NotBlank
@Email
private String email;
}
Limit Request Body Size
Configure a maximum request body size to prevent denial-of-service attacks through large payloads. Add this to application.properties:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
server.tomcat.max-http-form-post-size=10MB
Do Not Log Sensitive Request Bodies
Avoid logging the raw request body in production, especially when it contains passwords, tokens, or personal information. If you need logging for debugging, sanitize the body first.
HTTPS for Sensitive Data
Use HTTPS in production to encrypt request and response bodies in transit. Spring Boot can enforce this through security configuration.
Observability Checklist
When building APIs that handle request and response bodies, make sure you have proper observability in place:
- Structured logging with request IDs for tracing
- Log request and response Content-Type headers
- Log HTTP method, URL path, and status code for every request
- Measure and alert on slow requests (high latency)
- Track serialization/deserialization error rates
- Monitor payload sizes to catch unexpectedly large requests
- Add correlation IDs to propagate through distributed systems
Common Pitfalls / Anti-Patterns
Pitfall 1: Forgetting @Valid with @RequestBody
Without @Valid, Bean Validation annotations on your request DTO have no effect. Validation only fires when you explicitly add @Valid or @Validated.
Pitfall 2: Mixing @RequestBody with @RequestParam
You cannot read both the request body and query parameters from the same request using @RequestBody and @RequestParam together. The body is consumed after the first read. Use @RequestParam for simple values and @RequestBody for structured data.
Pitfall 3: Mutability Assumptions in Deserialized Objects
@RequestBody deserializes into an object that Spring manages. Do not assume the deserialized object is the same instance your service layer returns in the response. They are independent serialization cycles.
Pitfall 4: Ignoring the Accept Header
Clients should specify what response format they accept using the Accept header. Spring uses content negotiation to select the appropriate HttpMessageConverter. If you only support JSON but a client sends Accept: application/xml, you may get an unexpected response or a 406 Not Acceptable status.
Quick Recap Checklist
-
@RequestBodydeserializes the request body into a Java object -
@ResponseBodyserializes the return value into the response body -
@RestControllercombines@Controllerand@ResponseBodyfor all methods -
HttpEntityprovides access to both headers and body (read or write) -
RequestEntityextendsHttpEntitywith HTTP method and URI - Always add
@Validto@RequestBodyparameters to enable Bean Validation - Use
ResponseEntitywhen you need to set status codes or headers -
HttpMessageConverterdoes the actual serialization/deserialization work - Validate and sanitize all incoming request bodies
- Configure maximum request body size limits
Interview Questions
@RequestBody binds the entire HTTP request body to a method parameter, deserializing it into a Java object using an HttpMessageConverter. It is used for complex payloads like JSON or XML.
@RequestParam binds individual query parameters or form fields to method parameters. It is designed for simple values like strings, numbers, or booleans that appear in the URL query string or form data.
These two annotations are mutually exclusive in terms of what they read from the request. You cannot use both to read the same request body.
Spring Boot uses content negotiation to select the appropriate converter. For incoming requests, it examines the Content-Type header and matches it against the media types supported by each registered converter. For outgoing responses, it checks the Accept header and selects the first converter that supports that media type.
When multiple converters could handle the same type (for example, both Jackson JSON and Jackson XML support application/json), Spring Boot uses the order in which converters are registered. The auto-configured converters are ordered with more specific converters taking precedence.
When the request body is empty or contains malformed JSON, Spring throws a HttpMessageNotReadableException. This exception bubbles up through the handler adapter and is ultimately handled by Spring Boot's default error resolution mechanism, resulting in a 400 Bad Request response to the client.
You can customize this behavior by implementing a @ControllerAdvice that handles HttpMessageNotReadableException and returns a more structured error response with details about what went wrong.
Use HttpEntity or RequestEntity when you need access to HTTP headers in addition to the body. For example, when reading custom headers from an incoming request or setting specific headers on an outgoing response. RequestEntity is especially useful when making outbound HTTP calls with RestTemplate or WebClient where you need to specify the HTTP method, URL, headers, and body together as a single object.
For typical controller endpoints where you are just reading a request body or writing a response body, @RequestBody and @ResponseBody are simpler and more idiomatic.
Add the @Valid annotation alongside @RequestBody. This triggers Bean Validation (JSR-380) on the deserialized object before the controller method executes. If validation fails, Spring throws a MethodArgumentNotValidException.
You can then handle this exception in a @ControllerAdvice to return structured validation error responses. Validation annotations like @NotBlank, @Size, @Email, and @Pattern can be placed directly on the fields of your request DTO class.
Note that @ResponseBody does not have built-in validation integration. If you need to validate the response object before serializing it, you would perform that validation manually within the controller method.
HttpMessageConverter is responsible for converting HTTP request/response bodies to and from Java objects. Spring Boot auto-configures converters for JSON (Jackson), XML (Jaxb2), plain text, and binary formats.
To create a custom converter, implement the HttpMessageConverter interface with two methods: canRead()/canWrite() to declare which media types you handle, and read()/write() to perform the actual conversion.
Register it by implementing WebMvcConfigurer and overriding extendMessageConverters() to add your converter to the list. Place more specific converters before general ones since Spring uses the first match.
Content negotiation is the mechanism Spring uses to determine the response format (JSON, XML, etc.) based on client preferences. It examines the Accept header in the request and matches it against the media types supported by registered HttpMessageConverter instances.
If a client sends Accept: application/json, Spring selects the Jackson-based converter that produces JSON. If the client sends Accept: application/xml, it selects the XML converter. If no matching converter is found, Spring returns HTTP status 406 (Not Acceptable).
You can also configure a fallback order using WebMvcConfigurer with getDefaultContentType() and getSupportedMediaTypes() to handle cases where the Accept header is missing or ambiguous.
@Controller is a stereotype annotation that marks a class as a Spring MVC controller. By default, controller methods return a view name that gets resolved by a view resolver (Thymeleaf, JSP, etc.).
@RestController is a composed annotation that combines @Controller and @ResponseBody on every method. This means every return value is automatically serialized into the response body through HttpMessageConverter, without any view resolution.
Use @Controller when you need to return views (HTML pages). Use @RestController when building REST APIs where every method returns data (JSON/XML) rather than view names.
@ResponseBody tells Spring to serialize the return value directly to the response body using an HttpMessageConverter. It does not give you control over HTTP status codes or headers from within the method itself.
ResponseEntity is a wrapper that includes the status code, headers, and body all together. It gives you full control over the HTTP response. You can conditionally set the body, add custom headers, or return different status codes based on business logic.
Use @ResponseBody (or @RestController) for simple cases where you just need to return an object and the default status code (200 OK) is appropriate. Use ResponseEntity when you need to set specific status codes (like 201 Created, 204 No Content, 404 Not Found) or add custom headers.
The primary security concern with @RequestBody is that it deserializes client-provided JSON/XML into Java objects without any built-in filtering. This opens potential attack vectors:
- JSON Injection: Malicious JSON payloads can inject unexpected fields that get persisted or processed unsafely.
- Type Confusion: An attacker might send a payload that deserializes to a different class than expected (e.g., leveraging polymorphic deserialization).
- DoS via Large Payloads: Extremely large request bodies can exhaust server memory.
Mitigations include: validating all input with Bean Validation annotations, configuring maximum request body size limits in application.properties, avoiding logging raw request bodies, and using @JsonIgnoreProperties to block unexpected fields from deserialization.
No, you cannot have multiple @RequestBody parameters in a single method. The request body can only be consumed once — after the HttpMessageConverter reads and deserializes it into the first parameter, the input stream is exhausted and cannot be read again for a second parameter.
If you need to accept multiple pieces of data from a request, you have two options: combine them into a single DTO that wraps all the fields, or use @RequestParam for simple values alongside @RequestBody for the complex payload. Note that @RequestParam reads from query parameters or form data, not the request body.
Spring Boot configures CharacterEncodingFilter with UTF-8 by default when running on an embedded server. For request bodies, the encoding is determined by the Content-Type header's charset parameter, defaulting to ISO-8859-1 if not specified.
For responses, you should explicitly set the character encoding by using MediaType.APPLICATION_JSON_UTF8 (deprecated in newer Spring) or by configuring spring.http.encoding.enabled=true and spring.http.encoding.charset=UTF-8 in application.properties.
For explicit control, you can set the Content-Type header with charset directly: new MediaType("application", "json", StandardCharsets.UTF_8) when building a ResponseEntity.
@Valid triggers Jakarta Bean Validation (JSR-380) on the object deserialized by @RequestBody before the controller method executes. Spring creates a BeanValidator that validates each field annotated with constraint annotations like @NotBlank, @Size, @Email, @Min, @Max, and @Pattern.
If validation fails, Spring throws a MethodArgumentNotValidException (for single parameters) or BindException (for form binding). This exception should be handled by a @ControllerAdvice exception handler that returns a structured error response with field-level error details.
@Validated is the Spring-specific alternative that supports validation groups and can be used at both the class and method level for more fine-grained control.
The request flow in Spring MVC follows these steps:
- 1. DispatcherServlet receives HTTP request — This is the single front controller configured in the servlet container.
- 2. Handler Mapping looks up the controller —
RequestMappingHandlerMappingfinds the controller method that matches the URL and HTTP method. - 3. Handler Adapter invokes the controller —
RequestMappingHandlerAdapterprepares the invocation, resolving method arguments (like@RequestBody,@PathVariable,@RequestParam). - 4. HttpMessageConverter deserializes the request body — For
@RequestBodyparameters, the appropriate converter (e.g., Jackson for JSON) converts the request bytes to a Java object. - 5. Controller method executes — Business logic runs with the deserialized parameters.
- 6. Return value passes through HttpMessageConverter again — For
@ResponseBodyor@RestController, the return value is serialized to the response body. - 7. DispatcherServlet sends the HTTP response — The serialized data is written to the response stream.
For file uploads, use @RequestParam("file") MultipartFile file on your controller method. Spring Boot must have spring.servlet.multipart.enabled=true (default). Validate the file size using @Size or check file.isEmpty() before processing.
For file downloads, return ResponseEntity<Resource> or HttpEntity<byte[]> with proper headers set: Content-Type as the file's MIME type, Content-Disposition as attachment; filename="..." for downloads, and Content-Length with the file size when known.
Use InputStreamResource or ByteArrayResource as the body type depending on whether you're streaming or loading the entire file into memory. For large files, prefer streaming with InputStreamResource to avoid memory issues.
MultipartFile is a Spring-specific interface that provides convenient access to uploaded file metadata (original filename, content type, size, input stream). It is the preferred approach when using Spring's MultipartResolver for handling multipart/form-data requests.
javax.servlet.http.Part is the standard Servlet API interface for uploaded parts. It is less convenient than MultipartFile but works with the standard Servlet 3.0+ request.getPart() API without Spring's multipart processing.
Use MultipartFile in Spring applications as it provides better integration with Spring's error handling and validation. Use Part when you want to avoid Spring's multipart abstraction or are working in a non-Spring Servlet environment.
When Spring encounters malformed JSON in a request body, the HttpMessageConverter (typically Jackson) throws an exception such as JsonParseException or MismatchedInputException. Spring wraps this in an HttpMessageNotReadableException.
By default, Spring Boot's ErrorMvcAutoConfiguration returns a JSON error response with fields like timestamp, status, error, and message. For a 400 Bad Request caused by JSON parse errors, the message typically contains details about where parsing failed.
To customize error responses globally, implement a @ControllerAdvice with exception handlers for specific exception types. You can also implement ErrorController to have full control over the error response format and include additional fields like path, traceId, or custom application error codes.
Choose @RestController for straightforward REST endpoints where the default status code (200 OK) is always appropriate and you are just returning the resource representation. It keeps the code cleaner without explicit return ResponseEntity.ok(resource) wrapping every method.
Choose ResponseEntity when you need conditional response building based on business logic — for example, returning 201 Created with a Location header for newly created resources, 204 No Content for deletes, or 404 Not Found when a resource does not exist.
A common pattern is to use @RestController for read operations (GET) where 200 OK is always correct, and ResponseEntity for write operations (POST, PUT, DELETE) where the status code depends on the outcome.
Jackson polymorphic deserialization allows you to deserialize JSON into different Java classes based on a type field (like @JsonTypeInfo and @JsonSubTypes). This is useful for polymorphic APIs where the response type varies depending on the requested resource type.
The security concern is known as the Deserialization gadgets attack. If an attacker crafts a malicious JSON payload specifying a different class than expected (for example, a class that performs dangerous operations during construction or deserialization), they can execute arbitrary code or cause denial of service.
Mitigations include: avoiding @JsonTypeInfo on untrusted input when possible, using @JsonIgnoreProperties(ignoreUnknown = true) to ignore unexpected type information, configuring Jackson's default typing carefully with ObjectMapper.disableDefaultTyping() when not needed, and keeping Jackson and related libraries updated to patch known gadget vulnerabilities.
Further Reading
- Spring Boot Reference Documentation - Official Spring Boot documentation on Spring MVC
- HttpMessageConverter Documentation - Spring’s official guide to message converters
- Bean Validation API - Official Bean Validation (JSR-380) specification
- Jackson Annotations Guide - Comprehensive reference for Jackson serialization annotations
- Spring’s Content Negotiation - How Spring handles multiple content types
Conclusion
Spring Boot’s request and response handling is built on HttpMessageConverter—the abstraction that sits between Java objects and HTTP message bodies. @RequestBody deserializes incoming request bodies into Java objects, and @ResponseBody (or @RestController) serializes return values back into response bodies. These two annotations cover the vast majority of REST API use cases and do so without requiring any method-level configuration beyond specifying the target type.
The critical companion to @RequestBody is @Valid. Without it, validation constraints on your DTOs are silently ignored. Add @Valid on every @RequestBody parameter and handle MethodArgumentNotValidException in a @RestControllerAdvice to return structured error responses instead of letting malformed data reach your service layer. Similarly, always set explicit limits on request body size via spring.servlet.multipart.max-request-size to prevent memory exhaustion from oversized payloads.
For response construction, @RestController is the right choice for pure REST APIs where every method returns serialized data. Use ResponseEntity<T> when you need conditional status codes (201 Created, 204 No Content, 404 Not Found), custom headers, or when the same endpoint might return different status codes based on business logic. HttpEntity and RequestEntity are the lower-level alternatives when you need direct access to HTTP headers for either requests or responses.
Jackson is the default JSON processor under the hood, and understanding its behavior matters for security. Polymorphic deserialization (@JsonTypeInfo) enables flexible API designs but introduces deserialization gadget risks if applied to untrusted input. Treat request bodies as untrusted even after successful deserialization—validate all field values with Bean Validation constraints rather than assuming the incoming JSON matches your expected schema.
Category
Related Posts
Spring Boot Build Tools: Maven & Gradle
Configure Maven and Gradle for Spring Boot projects—plugins, dependency management, packaging JARs and WARs, and build automation essentials.
Embedded Web Servers in Spring Boot: Tomcat, Jetty, Undertow
Configure embedded servers in Spring Boot: compare Tomcat, Jetty, and Undertow, tune thread pools, enable access logs, and switch implementations.
JUnit 5 & Jupiter: Lifecycle, Nested & Parameterized Tests
Explore JUnit 5 Jupiter features: master test lifecycle annotations, organize tests with @Nested, and parameterize tests with @CsvSource and @MethodSource.