JavaRush /Курсы /Модуль 5. Spring /Формирование пользовательских сообщений об ошибках

Формирование пользовательских сообщений об ошибках

Модуль 5. Spring
9 уровень , 7 лекция
Открыта

Представьте, что вы пытаетесь заполнить форму на сайте, а система в ответ выдаёт: "Ошибка 400. Неправильный запрос. Поле user_input не соответствует требованиям валидации." Чувствуете этот холодный роботизированный тон? Пользователю сложно понять, что пошло не так и как это исправить.

Правильно оформленные сообщения об ошибках помогают:

  1. Направить пользователя на правильный путь, указав, что именно он сделал неправильно.
  2. Снизить уровень фрустрации и повысить удовлетворённость пользователя вашим приложением.
  3. Помочь разработчикам быстрее находить проблемы, используя логирование ошибок.

Теперь, когда мы знаем, зачем это нужно, приступим к делу.


Автоматическое формирование сообщений с 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;
    }
}

Теперь язык будет определяться автоматически на основании заголовка запроса.


Полезные советы

  1. Пишите понятные сообщения. Избегайте аббревиатур и технического жаргона. Пользователь должен понять, что ему делать.
  2. Логируйте ошибки. Если пользователь видит только часть информации, остальное логируйте на сервере — это поможет в поиске и устранении проблем.
  3. Тестируйте локализацию. Убедитесь, что сообщения корректно отображаются на всех поддерживаемых языках.

Теперь у нас есть мощный инструмент для вывода сообщений об ошибках, которые понятны и пользователю, и разработчику. Такие подходы делают приложение более профессиональным и дружественным. В следующей лекции мы рассмотрим, как настроить локализацию сообщений ещё глубже и подключить обработку ошибок на глобальном уровне.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ