Уявіть, що ви намагаєтеся заповнити форму на сайті, а система у відповідь видає: "Ошибка 400. Неправильный запрос. Поле user_input не соответствует требованиям валидации." Відчуваєте цей холодний роботизований тон? Користувачу важко зрозуміти, що пішло не так і як це виправити.
Правильно оформлені повідомлення про помилки допомагають:
- Направити користувача в правильному напрямку, вказавши, що саме він зробив неправильно.
- Зменшити рівень фрустрації і підвищити задоволеність користувача вашим застосунком.
- Допомогти розробникам швидше знаходити проблеми, використовуючи логування помилок.
Тепер, коли ми знаємо, навіщо це потрібно, приступимо до справи.
Автоматичне формування повідомлень з Bean Validation API
У попередніх лекціях ми вже додавали анотації для валідації даних. Нагадаємо простий приклад:
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class UserDto {
@NotNull(message = "Ім'я користувача не може бути порожнім")
@Size(min = 2, max = 30, message = "Ім'я користувача має бути від 2 до 30 символів")
private String username;
@NotNull(message = "Електронна пошта обов'язкова")
@Size(max = 50, message = "Електронна пошта не може містити більше 50 символів")
private String email;
// Геттери і сеттери
}
Тут message передає рядок, який буде показаний користувачу при порушенні правила. Це відмінний старт, але ми можемо зробити ще краще.
Локалізація повідомлень з використанням MessageSource
Якщо ви розробляєте застосунок для різних мов (наприклад, російська і англійська), було б круто підтримувати локалізацію. Дивіться, як це робиться.
Налаштування MessageSource
Додамо в проєкт клас конфігурації для локалізації:
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
@Configuration
public class MessageConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages/validation");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
Цей MessageSource буде читати файли локалізації. Наприклад, створимо файл validation.properties (за замовчуванням для англійської) і validation_ru.properties для російської мови:
validation.properties
username.notNull=Username is required
username.size=Username must be between 2 and 30 characters
email.notNull=Email is required
email.size=Email cannot exceed 50 characters
validation_ru.properties
username.notNull=Ім'я користувача не може бути порожнім
username.size=Ім'я користувача має бути від 2 до 30 символів
email.notNull=Електронна пошта обов'язкова
email.size=Електронна пошта не може містити більше 50 символів
Зміна DTO для використання локалізованих повідомлень
Тепер ми можемо використовувати message з ключами з файлів локалізації:
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class UserDto {
@NotNull(message = "{username.notNull}")
@Size(min = 2, max = 30, message = "{username.size}")
private String username;
@NotNull(message = "{email.notNull}")
@Size(max = 50, message = "{email.size}")
private String email;
// Геттери і сеттери
}
Ось так! Тепер повідомлення автоматично підтягуються залежно від мови користувача.
Валідація і формування повідомлень в контролерах
Припустимо, у нас є контролер для обробки запиту на створення користувача:
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody UserDto userDto) {
// Логіка збереження користувача...
return ResponseEntity.ok("Користувача успішно створено!");
}
}
Якщо користувач відправить некоректні дані, наприклад порожнє ім'я, Spring кине помилку MethodArgumentNotValidException. Щоб її обробити і повернути красиве повідомлення, додамо наступний обробник:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
}
Якщо користувач відправить запит:
{
"username": "",
"email": "anInvalidEmail@verylongemailthatexceedsfiftycharacters.com"
}
На виході ми отримаємо структурований і зрозумілий JSON:
{
"username": "Ім'я користувача не може бути порожнім",
"email": "Електронна пошта не може містити більше 50 символів"
}
Локалізація через HTTP-заголовки
Spring дозволяє змінювати мову в запитах через заголовок Accept-Language. Наприклад:
Accept-Language: en
У такому випадку повідомлення про помилки будуть відображатися англійською. Якщо вказати ru, то повідомлення автоматично переключаться на російську. Це робиться через вбудований LocaleResolver.
Додамо LocaleResolver:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import java.util.Locale;
@Configuration
public class LocaleConfig {
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
return localeResolver;
}
}
Тепер мова буде визначатися автоматично на основі заголовка запиту.
Корисні поради
- Пишіть зрозумілі повідомлення. Уникайте абревіатур і технічного жаргону. Користувач має зрозуміти, що йому робити.
- Логуйте помилки. Якщо користувач бачить лише частину інформації, решту логуйте на сервері — це допоможе в пошуку і усуненні проблем.
- Тестуйте локалізацію. Переконайтеся, що повідомлення коректно відображаються всіма підтримуваними мовами.
Тепер у нас є потужний інструмент для виводу повідомлень про помилки, які зрозумілі і користувачу, і розробнику. Такі підходи роблять застосунок більш професійним і дружнім. У наступній лекції ми розглянемо, як налаштувати локалізацію повідомлень ще глибше і підключити обробку помилок на глобальному рівні.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ