Коли ми створюємо REST API, часто потрібно обробляти дані, які передаються в запитах. Наприклад, створюємо новий запис або оновлюємо існуючий. Ці дані зазвичай передаються в тілі HTTP-запиту. Також, коли сервер відповідає клієнту, ми відправляємо дані назад, наприклад, у форматі JSON. В цей момент на сцену виходять анотації @RequestBody і @ResponseBody.
@RequestBody: допомагає "витягнути" дані з тіла HTTP-запиту і перетворити їх у Java-об'єкт, який ми можемо легко використовувати в коді.@ResponseBody: автоматично перетворює Java-об'єкт у JSON (або інший формат) і відправляє його в тіло HTTP-відповіді.
Це схоже на "телепортацію даних" між клієнтом і сервером. Ви передаєте JSON на сервер, і він чарівним чином перетворюється на нормальний Java-об'єкт. А сервер, у свою чергу, повертає об'єкт, який чудесним чином стає JSON.
Робота з тілом запиту: @RequestBody
Spring Boot використовує бібліотеку Jackson (за замовчуванням) для перетворення даних. Коли приходить HTTP-запит з JSON, анотація @RequestBody каже Spring: "Гей, візьми тіло запиту, розпарси його і передай мені Java-об'єкт!".
Припустімо, у нас є сутність User:
public class User {
private String name;
private int age;
// Геттери та сеттери
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;
}
}
Ми хочемо написати контролер, який буде отримувати об'єкт User в тілі запиту:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String createUser(@RequestBody User user) {
// Логуємо вхідні дані
System.out.println("Отримано користувача: " + user.getName() + ", вік: " + user.getAge());
return "Користувач " + user.getName() + " успішно створений!";
}
}
Клієнт відправляє POST-запит:
{
"name": "Іван",
"age": 30
}
І на сервері це перетворюється в об'єкт User. Жодного додаткового коду для парсингу JSON не потрібно — про все дбає Spring.
Валідація даних
Іноді передані дані можуть бути недопустимими. Наприклад, порожнє ім'я або вік менше нуля. У таких випадках ми можемо використати анотацію @Valid для автоматичної валідації:
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class User {
@NotNull(message = "Ім'я не повинно бути порожнім")
private String name;
@Min(value = 0, message = "Вік не може бути від'ємним")
private int age;
// Геттери та сеттери
}
І в контролері:
@PostMapping
public String createUser(@Valid @RequestBody User user) {
return "Користувач " + user.getName() + " успішно створений!";
}
Якщо клієнт відправить некоректні дані, наприклад:
{
"name": "",
"age": -1
}
Сервер поверне помилку 400 Bad Request з деталями:
{
"timestamp": "2023-10-22T10:12:45.123+00:00",
"status": 400,
"error": "Bad Request",
"messages": [
"Ім'я не повинно бути порожнім",
"Вік не може бути від'ємним"
]
}
Робота з відповідями: @ResponseBody
Коли потрібно відправити дані клієнту, @ResponseBody приходить на допомогу. Ця чарівна анотація підказує Spring, що Java-об'єкт треба перетворити в JSON (або інший формат) і зробити його частиною відповіді.
А знаєте, що цікаво? Якщо ви використовуєте @RestController, то Spring автоматично додасть @ResponseBody до всіх методів контролера. Саме тому REST-контролери пишуться швидше і елегантніше — Spring вже подбав про це за вас.
Продовжимо роботу з UserController. Тепер додамо метод для отримання користувача:
@GetMapping("/{id}")
public @ResponseBody User getUser(@PathVariable int id) {
// Заглушка: повернемо фіктивного користувача
User user = new User();
user.setName("Іван");
user.setAge(30);
return user;
}
Клієнт відправляє GET-запит:
GET /users/1
Відповідь сервера:
{
"name": "Іван",
"age": 30
}
Поради по роботі з @ResponseBody
- Ви можете повертати не тільки об'єкти, але й колекції. Наприклад:
@GetMapping
public List<User> getAllUsers() {
List<User> users = new ArrayList<>();
User user1 = new User();
user1.setName("Іван");
user1.setAge(30);
users.add(user1);
User user2 = new User();
user2.setName("Анна");
user2.setAge(25);
users.add(user2);
return users;
}
Відповідь сервера:
[
{
"name": "Іван",
"age": 30
},
{
"name": "Анна",
"age": 25
}
]
Особливості роботи анотації
Якщо ви використовуєте анотацію @ResponseBody з звичайним @Controller (не @RestController), не забудьте додати її до всіх методів, які повинні повертати дані в HTTP-відповіді. Наприклад:
@Controller
public class MyController {
@GetMapping("/hello")
public @ResponseBody String sayHello() {
return "Привіт!";
}
}
Типові помилки при використанні @RequestBody і @ResponseBody
Дуже важливо розуміти, як працюють ці анотації, щоб уникнути помилок. Наприклад:
- Помилка десеріалізації: якщо JSON, надісланий клієнтом, не відповідає структурі об'єкта, Spring викине виключення. Наприклад, якщо клієнт забув поле
"name", то ви отримаєте помилкуHttpMessageNotReadableException.- Рішення: Переконайтеся, що структура JSON узгоджується з моделлю об'єкта.
- Відмова від використання анотації
@Valid: без валідації ви можете отримати некоректні дані в базі. Використовуйте@Validі обробники помилок. - Ігнорування рівнів логування: логируйте JSON-запити та відповіді — це врятує ваш час при налагодженні.
Практичне застосування
@RequestBody і @ResponseBody — це два киті, на яких тримається REST API. Вони беруть на себе всю головну біль щодо обробки JSON — більше ніякого ручного парсингу і форматування! У реальних проєктах ці анотації стають вашими вірними помічниками при роботі з даними, їх перевіркою і перетворенням.
Неможливо уявити сучасний REST API без цих анотацій — це як намагатися писати код без IDE. Освоївши їх, ви зможете створювати елегантні і надійні API. І, звісно, не забувайте тестувати свої ендпоінти — але це вже зовсім інша історія!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ