Week 4 Day 3 — Request and Response Bodies
Goal
Today I want to understand how Spring MVC handles request bodies and response bodies.
Main questions:
- What is an HTTP request body?
- What is an HTTP response body?
- What does
@RequestBodydo? - What does
@ResponseBodydo? - Why does
@RestControllerreturn JSON automatically? - What is
HttpMessageConverter? - What is Jackson?
- What are DTOs?
- Why should REST APIs usually return DTOs, not entities?
- How does validation work with
@Valid? - What is
ResponseEntity? - What are common exam traps?
1. Quick Review from Week 4 Day 2
In Day 2, I learned:
@RequestMappingmaps HTTP requests to controller methods.@GetMapping,@PostMapping,@PutMapping,@PatchMapping, and@DeleteMappingare shortcut annotations.@PathVariablereads path values.@RequestParamreads query parameters.@RequestHeaderreads headers.@RequestBodyreads the HTTP body.consumeschecks requestContent-Type.producesdescribes response media type.Content-Typemeans request body format.Acceptmeans desired response format.
Memory sentence:
PathVariable = path.
RequestParam = query.
RequestHeader = header.
RequestBody = body.
Today I go deeper into request and response bodies.
2. What Is an HTTP Request Body?
The request body is the data sent by the client to the server.
Example:
POST /api/tasks
Content-Type: application/json
{
"title": "Learn Spring MVC",
"priority": "HIGH"
}
The body is:
{
"title": "Learn Spring MVC",
"priority": "HIGH"
}
The body is usually used with:
POST
PUT
PATCH
Common request body formats:
JSON
XML
form data
multipart file upload
plain text
For REST APIs, JSON is most common.
3. What Is an HTTP Response Body?
The response body is the data sent by the server back to the client.
Example response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 1,
"title": "Learn Spring MVC",
"priority": "HIGH"
}
The response body is:
{
"id": 1,
"title": "Learn Spring MVC",
"priority": "HIGH"
}
Memory sentence:
Request body comes from the client. Response body goes back to the client.
4. @RequestBody
@RequestBody tells Spring:
Read the HTTP request body and convert it into a Java object.
Request:
POST /api/tasks
Content-Type: application/json
{
"title": "Learn Spring MVC"
}
DTO:
public record CreateTaskRequest(String title) {
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Spring converts JSON into:
CreateTaskRequest
Memory sentence:
@RequestBodymeans JSON body to Java object.
5. What Happens Internally with @RequestBody?
Flow:
1. Client sends JSON body.
2. Spring MVC receives request.
3. Controller method has @RequestBody.
4. Spring checks Content-Type.
5. Spring finds suitable HttpMessageConverter.
6. Jackson converts JSON into Java object.
7. Controller method receives the Java object.
Example:
{
"title": "Learn Spring"
}
becomes:
new CreateTaskRequest("Learn Spring")
With Java records, Spring/Jackson can create the record from JSON fields.
6. @RequestBody and Content-Type
The client should send:
Content-Type: application/json
This tells Spring:
The request body is JSON.
Then Spring can choose the JSON converter.
If the endpoint expects JSON but the client sends:
Content-Type: text/plain
Spring may return:
415 Unsupported Media Type
Memory sentence:
Content-Typetells Spring how to read the request body.
7. Invalid JSON Body
Request:
POST /api/tasks
Content-Type: application/json
{
"title":
}
This JSON is invalid.
Spring cannot convert it into a Java object.
Usually response:
400 Bad Request
Why?
Because the client sent invalid request data.
8. Missing @RequestBody
Problem:
@PostMapping("/api/tasks")
public TaskDto create(CreateTaskRequest request) {
return taskService.create(request);
}
Request:
POST /api/tasks
Content-Type: application/json
{
"title": "Learn Spring"
}
Without @RequestBody, Spring may not read JSON from the body into CreateTaskRequest.
Fix:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Memory sentence:
For JSON request body, use
@RequestBody.
9. One Request Body
Usually, one controller method should have one @RequestBody.
Bad:
@PostMapping("/api/tasks")
public TaskDto create(
@RequestBody CreateTaskRequest request,
@RequestBody AuditInfo auditInfo
) {
return taskService.create(request, auditInfo);
}
Why bad?
One HTTP request has one body.
Spring cannot normally split one body into two separate @RequestBody objects.
Better:
public record CreateTaskCommand(
String title,
AuditInfo auditInfo
) {
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskCommand command) {
return taskService.create(command);
}
Memory sentence:
One HTTP request body should map to one request object.
10. @ResponseBody
@ResponseBody tells Spring:
Write the method return value directly to the HTTP response body.
Example:
@Controller
public class HelloController {
@GetMapping("/api/hello")
@ResponseBody
public String hello() {
return "hello";
}
}
Response body:
hello
Without @ResponseBody, Spring may treat "hello" as a view name.
11. @RestController Includes @ResponseBody
Very important exam sentence:
@RestController = @Controller + @ResponseBody
This:
@RestController
public class HelloController {
@GetMapping("/api/hello")
public String hello() {
return "hello";
}
}
means the return value goes directly into the response body.
So response body is:
hello
Spring does not look for a view named hello.
12. Response Body with Java Object
Controller:
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping("/{id}")
public TaskDto get(@PathVariable Long id) {
return new TaskDto(id, "Learn Spring MVC");
}
}
DTO:
public record TaskDto(Long id, String title) {
}
Response:
{
"id": 1,
"title": "Learn Spring MVC"
}
Spring converts the Java object to JSON automatically.
13. Who Converts Java Objects to JSON?
The converter is:
HttpMessageConverter
For JSON, Spring Boot usually uses:
Jackson
Flow:
Java object -> HttpMessageConverter -> JSON response body
Memory sentence:
HttpMessageConverterconverts between HTTP bodies and Java objects.
14. HttpMessageConverter
HttpMessageConverter converts in two directions:
Request body -> Java object
Java object -> Response body
Examples:
JSON -> Java object
Java object -> JSON
String -> text/plain
byte[] -> binary response
form data -> Java object or parameters
For a REST API:
JSON conversion is the most important.
15. Jackson
Jackson is a Java library for JSON serialization and deserialization.
Serialization means:
Java object -> JSON
Deserialization means:
JSON -> Java object
Example serialization:
new TaskDto(1L, "Learn Spring")
becomes:
{
"id": 1,
"title": "Learn Spring"
}
Example deserialization:
{
"title": "Learn Spring"
}
becomes:
CreateTaskRequest
Memory sentence:
Jackson reads JSON into Java and writes Java into JSON.
16. Spring Boot and Jackson
When I add:
implementation("org.springframework.boot:spring-boot-starter-web")
Spring Boot usually brings and configures Jackson automatically.
So this works without manual ObjectMapper setup:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Spring Boot auto-configures JSON conversion.
17. DTOs
DTO means:
Data Transfer Object
Simple definition:
A DTO is an object used to transfer data between layers or over the network.
In REST APIs, DTOs are used for request and response bodies.
Request DTO:
public record CreateTaskRequest(
String title,
String priority
) {
}
Response DTO:
public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}
Memory sentence:
DTOs define the API contract.
18. Request DTO vs Response DTO
Request DTO describes what the client sends.
public record CreateTaskRequest(
String title,
String priority
) {
}
Response DTO describes what the server returns.
public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}
They do not need to be the same.
Example:
Client sends:
{
"title": "Learn Spring",
"priority": "HIGH"
}
Server returns:
{
"id": 100,
"title": "Learn Spring",
"priority": "HIGH",
"status": "OPEN"
}
19. Why Use DTOs?
DTOs help with:
stable API contracts
hiding internal database structure
avoiding sensitive field leaks
avoiding lazy loading serialization problems
controlling response shape
separating API from persistence model
validation
versioning
Memory sentence:
DTOs protect the API boundary.
20. Entity vs DTO
Entity example:
@Entity
public class UserEntity {
@Id
private Long id;
private String email;
private String passwordHash;
private boolean internalAdminFlag;
}
Bad controller:
@GetMapping("/api/users/{id}")
public UserEntity getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow();
}
Problem:
passwordHash may be exposed
internal fields may leak
database model becomes API contract
lazy relationships may break serialization
Better response DTO:
public record UserDto(
Long id,
String email
) {
}
Controller:
@GetMapping("/api/users/{id}")
public UserDto getUser(@PathVariable Long id) {
return userService.findById(id);
}
Memory sentence:
Do not expose your database entity as your public API.
21. Mapping Entity to DTO
Entity:
public class TaskEntity {
private Long id;
private String title;
private String status;
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getStatus() {
return status;
}
}
DTO:
public record TaskDto(
Long id,
String title,
String status
) {
}
Manual mapping:
public TaskDto toDto(TaskEntity entity) {
return new TaskDto(
entity.getId(),
entity.getTitle(),
entity.getStatus()
);
}
For the exam, manual mapping is enough to understand.
In real projects, some teams use mapping libraries, but manual mapping is often clear and safe.
22. Request DTO Should Not Be Entity
Bad:
@PostMapping("/api/tasks")
public TaskEntity create(@RequestBody TaskEntity taskEntity) {
return taskRepository.save(taskEntity);
}
Why bad?
client can send fields it should not control
entity structure leaks into API
validation becomes unclear
security risk with internal fields
Better:
public record CreateTaskRequest(
String title,
String priority
) {
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
23. Validation
Validation checks whether request data is valid.
Example rules:
title must not be blank
email must be valid
age must be at least 18
price must be positive
date must be in the future
list must not be empty
In Spring Boot, validation usually uses Bean Validation annotations.
Need dependency:
implementation("org.springframework.boot:spring-boot-starter-validation")
Memory sentence:
Use validation to reject bad client input early.
24. Common Validation Annotations
Examples:
@NotNull
@NotBlank
@NotEmpty
@Size
@Min
@Max
@Positive
@PositiveOrZero
@Email
@Pattern
@Past
@Future
Common meaning:
| Annotation | Meaning |
|---|---|
@NotNull | value must not be null |
@NotBlank | string must contain non-whitespace text |
@NotEmpty | collection/string must not be empty |
@Size | size must be within range |
@Min | number must be at least value |
@Max | number must be at most value |
@Email | string must be email format |
@Positive | number must be greater than zero |
25. @NotNull vs @NotBlank vs @NotEmpty
This is an exam trap.
@NotNull
Value cannot be null.
@NotNull
private String name;
Invalid:
null
But this can be valid:
""
" "
@NotEmpty
Value cannot be null or empty.
Invalid:
null
""
empty list
But this can be valid for String:
" "
@NotBlank
For strings.
Invalid:
null
""
" "
Memory sentence:
For required text fields, use
@NotBlank.
26. Validation Example
Request DTO:
public record CreateTaskRequest(
@NotBlank
@Size(max = 100)
String title,
@NotBlank
String priority
) {
}
Controller:
@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
Important:
Validation runs because of @Valid.
Without @Valid, the annotations may not be checked in the controller method.
Memory sentence:
Validation annotations define rules.
@Validtriggers validation.
27. What Happens When Validation Fails?
Request:
POST /api/tasks
Content-Type: application/json
{
"title": "",
"priority": "HIGH"
}
DTO:
public record CreateTaskRequest(
@NotBlank String title,
@NotBlank String priority
) {
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Because title is blank, validation fails.
Usually response:
400 Bad Request
Spring throws a validation-related exception.
Later we will learn how to customize validation error responses with @ControllerAdvice.
28. Nested Validation
Request DTO:
public record CreateClientRequest(
@NotBlank
String name,
@Valid
AddressRequest address
) {
}
Nested DTO:
public record AddressRequest(
@NotBlank
String street,
@NotBlank
String city
) {
}
Important:
Use @Valid on the nested field to validate nested object.
Without nested @Valid, nested validation may not run.
Memory sentence:
Use
@Validfor nested objects too.
29. Validation on Lists
Example:
public record CreateOrderRequest(
@NotEmpty
List<@Valid OrderItemRequest> items
) {
}
Nested item:
public record OrderItemRequest(
@NotNull
Long productId,
@Positive
int quantity
) {
}
Meaning:
items list must not be empty
each item must be valid
quantity must be positive
30. @Validated
@Valid is most common for request body validation.
@Validated is a Spring annotation that can also trigger validation and supports validation groups.
Example:
@RestController
@Validated
public class TaskController {
@GetMapping("/api/tasks")
public List<TaskDto> list(@RequestParam @Min(0) int page) {
return taskService.findPage(page);
}
}
For many controller request bodies:
@Valid @RequestBody CreateTaskRequest request
is enough.
Exam-safe sentence:
Use
@Validwith@RequestBodyto validate request DTOs.@Validatedis Spring’s variant and supports validation groups.
31. Validation on Query Parameters
Example:
@RestController
@Validated
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping
public List<TaskDto> list(
@RequestParam(defaultValue = "0") @Min(0) int page,
@RequestParam(defaultValue = "20") @Min(1) @Max(100) int size
) {
return taskService.findPage(page, size);
}
}
Important:
For method parameter validation, @Validated on the controller class is commonly used.
32. ResponseEntity
ResponseEntity represents the full HTTP response.
It can control:
status code
headers
body
Example:
@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
Response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 1,
"title": "Learn Spring"
}
Memory sentence:
ResponseEntitygives control over status, headers, and body.
33. Returning 200 OK
@GetMapping("/api/tasks/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
TaskDto task = taskService.findById(id);
return ResponseEntity.ok(task);
}
Meaning:
HTTP 200 OK with body
34. Returning 201 Created
@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
Meaning:
HTTP 201 Created with body
For create operations, 201 Created is usually better than 200 OK.
35. Returning Location Header
For newly created resources, I can return a Location header.
@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
URI location = URI.create("/api/tasks/" + created.id());
return ResponseEntity
.created(location)
.body(created);
}
Response:
HTTP/1.1 201 Created
Location: /api/tasks/1
Content-Type: application/json
Memory sentence:
ResponseEntity.created(location)returns 201 with a Location header.
36. Returning 204 No Content
Delete endpoint:
@DeleteMapping("/api/tasks/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
taskService.delete(id);
return ResponseEntity.noContent().build();
}
Meaning:
HTTP 204 No Content
Important:
204 response should not have a response body.
37. Returning 404 Not Found
Simple example:
@GetMapping("/api/tasks/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
Optional<TaskDto> task = taskService.findById(id);
return task
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
Meaning:
If task exists -> 200 OK with body
If task does not exist -> 404 Not Found
In larger apps, this is often handled with exceptions and @ControllerAdvice.
38. @ResponseStatus
For fixed status codes, I can use @ResponseStatus.
Example:
@PostMapping("/api/tasks")
@ResponseStatus(HttpStatus.CREATED)
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
This returns:
201 Created
Use @ResponseStatus when status is fixed.
Use ResponseEntity when status, headers, or body need more control.
39. ResponseEntity vs @ResponseStatus
| Topic | ResponseEntity | @ResponseStatus |
|---|---|---|
| Status | dynamic or fixed | fixed |
| Headers | easy to control | not ideal |
| Body | explicit | method return value |
| Common use | flexible responses | simple fixed status |
| Example | 200 or 404 | always 201 |
Memory sentence:
Use
ResponseEntityfor control. Use@ResponseStatusfor simple fixed status.
40. Full Create Flow with Validation
Request:
POST /api/tasks
Content-Type: application/json
Accept: application/json
{
"title": "Learn validation",
"priority": "HIGH"
}
Request DTO:
public record CreateTaskRequest(
@NotBlank
@Size(max = 100)
String title,
@NotBlank
String priority
) {
}
Controller:
@PostMapping(
value = "/api/tasks",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
URI location = URI.create("/api/tasks/" + created.id());
return ResponseEntity.created(location).body(created);
}
Flow:
1. Client sends JSON.
2. Spring checks Content-Type.
3. Jackson converts JSON to CreateTaskRequest.
4. Bean Validation checks @NotBlank and @Size.
5. Controller method runs.
6. Service creates task.
7. Controller returns ResponseEntity.
8. Jackson converts TaskDto to JSON.
9. Response status is 201 Created.
10. Location header points to new resource.
41. Common Error: Missing Validation Starter
Problem:
public record CreateTaskRequest(
@NotBlank String title
) {
}
But annotation is not found or validation does not work.
Possible missing dependency:
implementation("org.springframework.boot:spring-boot-starter-validation")
Memory sentence:
For Bean Validation in Spring Boot, add validation starter.
42. Common Error: Missing @Valid
Problem:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
DTO:
public record CreateTaskRequest(
@NotBlank String title
) {
}
The annotation exists, but validation may not run.
Fix:
@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Memory sentence:
Add
@Validto trigger request body validation.
43. Common Error: Returning Entity with Lazy Relations
Entity:
@Entity
public class ClientEntity {
@OneToMany(mappedBy = "client")
private List<TaskEntity> tasks;
}
Controller:
@GetMapping("/api/clients/{id}")
public ClientEntity getClient(@PathVariable Long id) {
return clientRepository.findById(id).orElseThrow();
}
Possible problems:
lazy loading exception
infinite recursion
huge response body
sensitive fields exposed
API depends on database model
Better:
public record ClientDto(
Long id,
String name
) {
}
Return ClientDto.
44. Common Error: Infinite JSON Recursion
Example:
public class Client {
private List<Task> tasks;
}
public class Task {
private Client client;
}
If both sides are serialized:
Client -> tasks -> task -> client -> tasks -> task -> ...
This can cause infinite recursion.
DTOs solve this cleanly by controlling the response shape.
Memory sentence:
DTOs prevent many JSON serialization problems.
45. Common Error: Wrong Field Names
JSON:
{
"task_title": "Learn Spring"
}
Java record:
public record CreateTaskRequest(
String title
) {
}
Jackson expects:
{
"title": "Learn Spring"
}
If the JSON field name differs, the value may not bind as expected.
Possible fix:
public record CreateTaskRequest(
@JsonProperty("task_title")
String title
) {
}
Then JSON task_title maps to Java title.
46. Common Jackson Annotations
Useful annotations:
@JsonProperty
@JsonIgnore
@JsonFormat
@JsonInclude
Examples:
public record UserDto(
Long id,
@JsonProperty("email_address")
String email
) {
}
public class InternalUserDto {
private Long id;
@JsonIgnore
private String internalToken;
}
For certification, know the idea:
Jackson annotations can customize JSON serialization and deserialization.
47. Dates and JSON
DTO:
public record TaskDto(
Long id,
String title,
LocalDate dueDate
) {
}
JSON may look like:
{
"id": 1,
"title": "Learn Spring",
"dueDate": "2026-07-07"
}
For date/time formatting, use standard ISO formats when possible.
Custom format example:
public record TaskDto(
Long id,
String title,
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate dueDate
) {
}
48. Request Body and Immutability
Java records are good DTOs because they are:
compact
immutable
clear
easy to serialize/deserialize
good for request/response data
Example:
public record CreateTaskRequest(
@NotBlank String title,
@NotBlank String priority
) {
}
For many modern Spring Boot apps, records are a clean DTO style.
49. Good REST Controller Example
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping("/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
return taskService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
URI location = URI.create("/api/tasks/" + created.id());
return ResponseEntity.created(location).body(created);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
taskService.delete(id);
return ResponseEntity.noContent().build();
}
}
Request DTO:
public record CreateTaskRequest(
@NotBlank
@Size(max = 100)
String title,
@NotBlank
String priority
) {
}
Response DTO:
public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}
50. Common HTTP Status Codes with Bodies
| Situation | Status |
|---|---|
| Successful GET with body | 200 OK |
| Successful create | 201 Created |
| Successful delete without body | 204 No Content |
| Validation error | 400 Bad Request |
| Invalid JSON | 400 Bad Request |
| Resource not found | 404 Not Found |
| Unsupported request body type | 415 Unsupported Media Type |
| Unsupported response type requested | 406 Not Acceptable |
51. Common Exam Traps
Trap 1
@RequestBody reads the HTTP request body.
It does not read query parameters.
Trap 2
@ResponseBody writes the return value to the HTTP response body.
It does not render a view.
Trap 3
@RestController includes @ResponseBody.
Trap 4
HttpMessageConverter converts between HTTP bodies and Java objects.
Trap 5
Jackson usually handles JSON in Spring Boot MVC apps.
Trap 6
Content-Type describes the request body format.
Accept describes the response format the client wants.
Trap 7
Validation annotations alone are not enough.
Use @Valid.
Trap 8
For Bean Validation support, add spring-boot-starter-validation.
Trap 9
DTOs are better than exposing JPA entities directly.
Trap 10
Use ResponseEntity to control status, headers, and body.
52. Real Exam Question: @RequestBody
Question:
What does @RequestBody do?
Answer:
@RequestBody tells Spring to read the HTTP request body and convert it into a Java object using an HttpMessageConverter.
53. Real Exam Question: @ResponseBody
Question:
What does @ResponseBody do?
Answer:
@ResponseBody tells Spring to write the method return value directly to the HTTP response body instead of treating it as a view name.
54. Real Exam Question: @RestController
Question:
What is @RestController equivalent to?
Answer:
@RestController is equivalent to @Controller plus @ResponseBody.
55. Real Exam Question: HttpMessageConverter
Question:
What does HttpMessageConverter do?
Answer:
It converts between HTTP request/response bodies and Java objects, such as JSON to Java object and Java object to JSON.
56. Real Exam Question: Jackson
Question:
What is Jackson used for in Spring MVC REST APIs?
Answer:
Jackson is usually used to serialize Java objects to JSON and deserialize JSON into Java objects.
57. Real Exam Question: DTO
Question:
What is a DTO?
Answer:
A DTO, or Data Transfer Object, is an object used to transfer data, especially across API boundaries. In REST APIs, request DTOs describe what clients send, and response DTOs describe what the server returns.
58. Real Exam Question: Entity vs DTO
Question:
Why should REST APIs usually return DTOs instead of JPA entities?
Answer:
DTOs protect the API boundary, avoid exposing internal database structure, prevent sensitive field leaks, avoid lazy loading and infinite recursion problems, and allow the API contract to evolve separately from the persistence model.
59. Real Exam Question: Validation
Question:
How do I validate a JSON request body?
Answer:
Add validation annotations to the request DTO, add spring-boot-starter-validation, and use @Valid with @RequestBody.
Example:
public record CreateTaskRequest(
@NotBlank String title
) {
}
@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
60. Real Exam Question: ResponseEntity
Question:
Why use ResponseEntity?
Answer:
ResponseEntity allows a controller method to control the HTTP status code, response headers, and response body.
61. Real Exam Question: 201 Created
Question:
How can I return 201 Created with a body?
Answer:
return ResponseEntity.status(HttpStatus.CREATED).body(createdDto);
or with location:
return ResponseEntity.created(location).body(createdDto);
62. Real Exam Question: 204 No Content
Question:
How can I return 204 No Content?
Answer:
return ResponseEntity.noContent().build();
63. Interview Answer
Question:
How does Spring convert a JSON request body into a Java object?
Good answer:
When a controller method parameter is annotated with @RequestBody, Spring reads the HTTP request body and uses an HttpMessageConverter to convert it into the target Java type. In a typical Spring Boot MVC application, Jackson is auto-configured and used as the JSON converter. The request Content-Type, such as application/json, helps Spring choose the correct converter.
64. Interview Answer
Question:
How does Spring convert a Java object into a JSON response?
Good answer:
In a REST controller, the method return value is written to the response body because @RestController includes @ResponseBody. Spring then uses an HttpMessageConverter, usually Jackson, to serialize the Java object into JSON. The response format can also be influenced by content negotiation and the Accept header.
65. Interview Answer
Question:
Why do you use DTOs in REST APIs?
Good answer:
I use DTOs to define a clear API contract and to separate the external API from the internal persistence model. DTOs help avoid exposing sensitive fields, prevent lazy loading and infinite recursion issues, control the response shape, and make it easier to evolve the API without changing database entities directly.
66. Interview Answer
Question:
How do you validate request bodies in Spring Boot?
Good answer:
I add spring-boot-starter-validation, put Bean Validation annotations such as @NotBlank, @Size, or @Email on the request DTO, and annotate the controller parameter with @Valid @RequestBody. If validation fails, Spring usually returns a 400 Bad Request. For custom error responses, I can use @ControllerAdvice, which we will study later.
67. Interview Answer
Question:
What is
ResponseEntityused for?
Good answer:
ResponseEntity represents the full HTTP response. It lets me control the status code, headers, and body. For example, I can return 201 Created with a Location header after creating a resource, return 204 No Content after deleting something, or return 404 Not Found when a resource does not exist.
68. Tiny Code Practice
Create request and response DTOs:
public record CreateTaskRequest(
@NotBlank
@Size(max = 100)
String title,
@NotBlank
String priority
) {
}
public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}
Create controller:
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@PostMapping
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = new TaskDto(
1L,
request.title(),
request.priority(),
"OPEN"
);
URI location = URI.create("/api/tasks/" + created.id());
return ResponseEntity.created(location).body(created);
}
}
Questions:
- What does
@RequestBodydo? - What does
@Validdo? - What status code is returned?
- What header is returned?
- Who converts
TaskDtoto JSON?
Answers:
- It reads the JSON request body into
CreateTaskRequest. - It triggers validation.
201 Created.Location: /api/tasks/1.HttpMessageConverter, usually Jackson.
69. Tiny Bug Practice 1
Problem:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
DTO:
public record CreateTaskRequest(
@NotBlank String title
) {
}
Request:
{
"title": ""
}
Validation does not happen.
Question:
What is missing?
Answer:
@Valid is missing.
Fix:
@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
70. Tiny Bug Practice 2
Problem:
@GetMapping("/api/users/{id}")
public UserEntity getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow();
}
Question:
Why is this risky?
Answer:
Returning a JPA entity directly can expose internal fields, leak sensitive data, cause lazy loading problems, cause infinite JSON recursion, and tightly couple the API to the database model. Return a DTO instead.
71. Tiny Bug Practice 3
Problem:
@Controller
@RequestMapping("/api/hello")
public class HelloController {
@GetMapping
public String hello() {
return "hello";
}
}
Expected response body:
hello
But Spring tries to resolve a view.
Question:
Why?
Answer:
@Controller does not automatically include @ResponseBody. Spring treats the returned String as a view name. Use @RestController or add @ResponseBody.
Practice Questions and Answers
Question 1
What is an HTTP request body?
Answer:
The HTTP request body is the data sent by the client to the server, often JSON for POST, PUT, or PATCH requests.
Question 2
What is an HTTP response body?
Answer:
The HTTP response body is the data sent by the server back to the client, often JSON in REST APIs.
Question 3
What does @RequestBody do?
Answer:
@RequestBody tells Spring to read the HTTP request body and convert it into a Java object using an HttpMessageConverter.
Question 4
What does @ResponseBody do?
Answer:
@ResponseBody tells Spring to write the method return value directly to the HTTP response body instead of treating it as a view name.
Question 5
What does @RestController include?
Answer:
@RestController includes @Controller and @ResponseBody.
Question 6
What is HttpMessageConverter?
Answer:
HttpMessageConverter converts between HTTP request/response bodies and Java objects.
Question 7
What is Jackson?
Answer:
Jackson is a JSON library used by Spring Boot to convert Java objects to JSON and JSON to Java objects.
Question 8
What is serialization?
Answer:
Serialization means converting a Java object into JSON or another external format.
Question 9
What is deserialization?
Answer:
Deserialization means converting JSON or another external format into a Java object.
Question 10
What is a DTO?
Answer:
A DTO is a Data Transfer Object used to transfer data across layers or API boundaries.
Question 11
What is the difference between request DTO and response DTO?
Answer:
A request DTO describes what the client sends to the server. A response DTO describes what the server returns to the client.
Question 12
Why should REST APIs usually return DTOs instead of entities?
Answer:
DTOs avoid exposing internal database structure, prevent sensitive field leaks, reduce lazy loading and infinite recursion issues, control response shape, and separate the API contract from the persistence model.
Question 13
How do I validate a request body?
Answer:
Add validation annotations to the request DTO, add spring-boot-starter-validation, and use @Valid with @RequestBody.
Question 14
What is the difference between @NotNull, @NotEmpty, and @NotBlank?
Answer:
@NotNull means the value cannot be null. @NotEmpty means it cannot be null or empty. @NotBlank means a string cannot be null, empty, or only whitespace.
Question 15
What happens if validation fails?
Answer:
Spring usually returns 400 Bad Request.
Question 16
What does ResponseEntity control?
Answer:
ResponseEntity controls HTTP status code, headers, and body.
Question 17
How do I return 201 Created?
Answer:
Use:
return ResponseEntity.status(HttpStatus.CREATED).body(body);
or:
return ResponseEntity.created(location).body(body);
Question 18
How do I return 204 No Content?
Answer:
Use:
return ResponseEntity.noContent().build();
Question 19
When should I use @ResponseStatus?
Answer:
Use @ResponseStatus when the response status is fixed and simple.
Question 20
What is the difference between ResponseEntity and @ResponseStatus?
Answer:
ResponseEntity gives flexible control over status, headers, and body. @ResponseStatus sets a fixed status for a controller method or exception.
Final Memory Sentences
- Request body is data from client to server.
- Response body is data from server to client.
@RequestBodyreads body into a Java object.@ResponseBodywrites return value into the response body.@RestControllerequals@Controllerplus@ResponseBody.HttpMessageConverterconverts between HTTP bodies and Java objects.- Jackson usually converts JSON to Java and Java to JSON.
- Serialization means Java to JSON.
- Deserialization means JSON to Java.
- DTOs define the API contract.
- Request DTO and response DTO can be different.
- REST APIs should usually return DTOs, not entities.
- Add
spring-boot-starter-validationfor Bean Validation. - Validation annotations need
@Validto run on request bodies. @NotBlankis best for required text fields.ResponseEntitycontrols status, headers, and body.201 Createdis good for create operations.204 No Contentis good for successful delete without body.@ResponseStatusis good for simple fixed status.