When we build a REST API, we often need to handle data that comes in requests — for example, creating a new record or updating an existing one. That data is usually sent in the HTTP request body. Also, when the server replies to the client, we send data back, typically as JSON. This is where the annotations @RequestBody and @ResponseBody come in.
@RequestBody: helps "extract" data from the HTTP request body and converts it into a Java object you can work with easily in your code.@ResponseBody: automatically converts a Java object to JSON (or another format) and puts it into the HTTP response body.
It's kinda like "data teleportation" between client and server. You send JSON to the server and it magically becomes a normal Java object. And the server sends back an object that magically becomes JSON.
Working with the request body: @RequestBody
Spring Boot uses the Jackson library (by default) for data conversion. When an HTTP request with JSON arrives, the @RequestBody annotation tells Spring: "Hey, take the request body, parse it and give me a Java object!".
Say we have an entity User:
public class User {
private String name;
private int age;
// Getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
We want to write a controller that accepts a User in the request body:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String createUser(@RequestBody User user) {
// Log incoming data
System.out.println("Received user: " + user.getName() + ", age: " + user.getAge());
return "User " + user.getName() + " created successfully!";
}
}
The client sends a POST request:
{
"name": "Ivan",
"age": 30
}
On the server this becomes a User object. No extra JSON parsing code required — Spring handles it all.
Data validation
Sometimes incoming data can be invalid. For example, an empty name or a negative age. In those cases you can use @Valid for automatic validation:
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class User {
@NotNull(message = "Name must not be empty")
private String name;
@Min(value = 0, message = "Age cannot be negative")
private int age;
// Getters and setters
}
And in the controller:
@PostMapping
public String createUser(@Valid @RequestBody User user) {
return "User " + user.getName() + " created successfully!";
}
If the client sends invalid data, for example:
{
"name": "",
"age": -1
}
The server will return a 400 Bad Request with details:
{
"timestamp": "2023-10-22T10:12:45.123+00:00",
"status": 400,
"error": "Bad Request",
"messages": [
"Name must not be empty",
"Age cannot be negative"
]
}
Working with responses: @ResponseBody
When you need to send data back to the client, @ResponseBody helps. This handy annotation tells Spring that a Java object should be turned into JSON (or another format) and put into the response.
Here’s a neat thing: if you use @RestController, Spring automatically applies @ResponseBody to all controller methods. That's why REST controllers are quicker and cleaner — Spring already took care of it for you.
Continuing with UserController. Let's add a method to get a user:
@GetMapping("/{id}")
public @ResponseBody User getUser(@PathVariable int id) {
// Stub: return a fake user
User user = new User();
user.setName("Ivan");
user.setAge(30);
return user;
}
The client sends a GET request:
GET /users/1
Server response:
{
"name": "Ivan",
"age": 30
}
Tips for using @ResponseBody
- You can return not only single objects but collections too. For example:
@GetMapping
public List<User> getAllUsers() {
List<User> users = new ArrayList<>();
User user1 = new User();
user1.setName("Ivan");
user1.setAge(30);
users.add(user1);
User user2 = new User();
user2.setName("Anna");
user2.setAge(25);
users.add(user2);
return users;
}
Server response:
[
{
"name": "Ivan",
"age": 30
},
{
"name": "Anna",
"age": 25
}
]
Behavior notes
If you use @ResponseBody with a regular @Controller (not @RestController), remember to add it to every method that should return data in the HTTP response. For example:
@Controller
public class MyController {
@GetMapping("/hello")
public @ResponseBody String sayHello() {
return "Hello!";
}
}
Common mistakes when using @RequestBody and @ResponseBody
It's important to understand how these annotations work to avoid mistakes. For example:
- Deserialization error: if the JSON sent by the client doesn't match the object structure, Spring will throw an exception. For example, if the client forgot the
"name"field, you'll getHttpMessageNotReadableException.- Fix: Make sure the JSON structure matches your model.
- Skipping
@Valid: without validation you might store bad data in your DB. Use@Validand error handlers. - Ignoring logging levels: log JSON requests and responses — it'll save you time while debugging.
Practical use
@RequestBody and @ResponseBody are two pillars of REST APIs. They take care of all the JSON handling — no more manual parsing or formatting! In real projects these annotations become your reliable helpers for processing, validating and converting data.
It's hard to imagine a modern REST API without these annotations — it's like trying to code without an IDE. Once you master them, you can build clean and reliable APIs. And of course, don't forget to test your endpoints — but that's a whole other story!
GO TO FULL VERSION