Рубрики
Без рубрики

Как создавать пользовательские исключения с помощью Spring Boot

В процессе создания моего блога я хочу иметь возможность сообщать пользователю, если что-то пошло не так. I… С пометкой codenewbie, java, learning.

В процессе создания моего блога я хочу иметь возможность сообщать пользователю, если что-то пошло не так. Это может варьироваться от стандартной ошибки 404, пользовательского сообщения, до моих собственных пользовательских исключений. Я покажу вам, как создавать пользовательские исключения с помощью Spring Boot, чтобы в следующий раз вы могли сделать это сами!

Обработка исключений

Во-первых, я хочу определить, как будет выглядеть мой ответ на исключение для клиента. Я делаю это с помощью простого класса с именем Exception Response и некоторыми свойствами.

public class ExceptionResponse {
    private Date timestamp;
    private String message;
    private String details;

    public ExceptionResponse(Date timestamp, String message, String details) {
        this.timestamp = timestamp;
        this.message = message;
        this.details = details;
    }
}

Все свойства также имеют методы get, но я исключил это из фрагмента кода. Поскольку я не ожидаю, что буду изменять значения свойств, я не реализовал никаких методов set.

Обработчик Ответа на исключение

Когда класс для ответа на исключение завершен, теперь я могу начать с обработки генерируемых исключений. Для этого я создал Обработчик ответа на исключение который наследует класс ResponseEntityExceptionHandler . Он также снабжен комментариями @ControllerAdvice . Эта аннотация позволяет мне создать один класс, который обрабатывает все исключения для каждого контроллера в моем приложении. Итак, я могу добавить все исключения, которые будут обрабатываться в этом классе, сохраняя его в одном месте для удобства обслуживания.

Итак, перво-наперво, я хочу иметь один универсальный метод для обработки любого исключения, создаваемого моим приложением. Что бы ни случилось, я все равно хочу уведомить пользователя.

@ControllerAdvice
@RestController
public class ExceptionResponseHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Exception.class)
    public final ResponseEntity handleAllExceptions(Exception ex, WebRequest req) {
        ExceptionResponse exceptionResponse = new ExceptionResponse(
            new Date(),
            ex.getMessage(),
            req.Description(false)
        );
        return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

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

Пользовательские исключения в Spring Boot

После завершения общей настройки, наконец, пришло время перейти к пользовательским исключениям в Spring Boot. Для этого мне нужно для начала создать класс исключений. В моем случае я хочу уведомить, что публикуемая запись в блоге уже существует. Тем не менее, я совершенно уверен, что дублирование элементов не будет применяться только к сообщениям в блоге. Поэтому я сделал его немного более общим: ResourceAlreadyExistsException .

Класс ResourceAlreadyExistsException является относительно простым. Он расширяет класс RuntimeException , и вы можете добавить к нему столько параметров, сколько захотите. Я изложил это кратко, вот так.

public class ResourceAlreadyExistsException extends RuntimeException {

    public ResourceAlreadyExistsException(String property, String value) {
        super(String.format(
            "Resource with property %s and value %s already exists." +
            "Make sure to insert a unique value for %s",
            property, value, property));
    }
}

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

Выбор Кода Ошибки

Для каждого исключения существует подходящий код ошибки. Однако в данном случае я не был уверен, что использовать. Естественно, я поискал вокруг и наткнулся на вопрос в StackOverflow, обсуждающий этот самый пример.

Принятым ответом был код конфликта ошибки 409. Использование этого кода означает, что запрос не может быть завершен из-за конфликта в текущем состоянии ресурса. Однако, размышляя об этом значении, я чувствую, что в моем случае это неправильный код ошибки. Состояние самого ресурса не является проблемой.

Другой вариант – использовать обратный запрос с ошибкой 400, но это тоже кажется неправильным. Этот код предполагает, что сервер не понимает отправляемый запрос и, следовательно, не может его обработать. Здесь это тоже не так, потому что сервер прекрасно понимает запрос.

Чуть дальше по посту я наткнулся на интересный ответ: использование ошибки 422 Unprocessable Entity. Об этом коде ошибки сказано следующее в WebDAV RFC 4918 :

Код состояния 422 (Необработанный объект) означает, что сервер понимает тип содержимого объекта запроса (следовательно, код состояния 415 (Неподдерживаемый тип носителя) неуместен), и синтаксис объекта запроса правильный (следовательно, код состояния 400 (Неверный запрос) неуместен), но не смог выполнить обработайте содержащиеся в нем инструкции.

Определение этого кода ошибки, по-видимому, соответствует ситуации, с которой я столкнулся. Сервер понимает запрос; это действительный запрос, но не может обработать объект. Он не может обработать объект, потому что он является дубликатом.

Обновление обработчика ответов на исключения

Наконец, я решил использовать ошибку 422 для моего исключения ResourceAlreadyExistsException . Тем не менее, мне нужно подключить это сообщение об ошибке к Обработчику ответа на исключение . Дополнительный метод очень похож на метод, который я создал ранее для обработки всех исключений. На самом деле, вы можете легко скопировать и вставить этот метод для всех имеющихся у вас исключений. Все, что вам нужно сделать, это изменить класс исключения на ваше исключение и изменить статус Http.<код> .

@ExceptionHandler(ResourceAlreadyExistsException.class)
public final ResponseEntity handleResourceAlreadyExistsException(
    ResourceAlreadyExistsException ex, WebRequest req) {
    ExceptionResponse exceptionResponse = new ExceptionResponse(
        new Date(),
        ex.getMessage(),
        req.getDescription(false)
    );
    return new ResponseEntity<>(exceptionResponse, HttpStatus.UNPROCESSABLE_ENTITY);

Вывод

Выяснить, как заставить пользовательские исключения работать в Spring Boot, безусловно, было интересно. Я рад, что это решение также можно использовать повторно для нескольких проектов! Следующий шаг – выяснить, как писать модульные тесты для слоев. Предпочтительно, я хочу писать модульные тесты с помощью mocks, чтобы я мог протестировать функциональность без каких-либо зависимостей.

Оригинал: “https://dev.to/shadowphoenix/how-to-throw-custom-exceptions-with-spring-boot-2dd4”