Best practices обраотки ошибок в spring boot

У меня вопрос по поводу того, как обычно выстраивают архитектуру обработки исключений в RESTful API на Spring Boot.

Я делаю API для чата, и у пользователей есть возможность создавать чаты.

В контроллере ChatController есть метод create, который принимает запрос и передаёт данные вроде имени чата и его описания в метод create сервиса ChatService, в котором будет выполняться вся логика.

Чтобы узнать, имеет ли пользователь права на создание чатов, должна быть выполнена проверка, есть ли в списке ролей пользователя User хотя-бы одна роль Role, которой разрешено совершать действие Action с именем name = "CREATE_CHAT"

Для этого из базы данных нужно получить сущность Action с соответствующим именем, поэтому сервис вызывает метод findByName сервиса ActionService, а вот код этого сервиса:

public Action findByName(String name) {
        return actionRepository.findByName(name).orElseThrow(
                () -> new EntityNotFoundException("Action with name " + name + " was not found.")
        );
    }

В итоге ControllerAdvice отловит это исключение и в теле ответа в поле message укажет именно сообщение об ошибке, в котором написано, что Action с таким-то именем не найден, хотя я хотел бы, чтобы в ответе отправилось, например, такое сообщение: "Internal server error: cannot verify if you are allowed to create chats.", а засунуть это сообщение в метод ActionService.findByName я не могу, потому что этот метод используется далеко не только тогда, когда нужно создать новый чат.

Как поступают в таких случаях? Очевидное решение - каждый раз отлавливать исключение при любом вызове ActionService.findByName, после чего выбрасывать новое исключение, дополняя его информативным сообщением и в качестве cause передавая пойманное исключение из findByName

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

Может есть какой-нибудь волшебный способ сделать всё хорошо и при этом коротко?


Ответы (1 шт):

Автор решения: tequillaz

Оборачивание в try-catch по каждому месту вызова действительно выглядит плохой идеей, но какую-то обработку по месту вызова ActionService делать необходимо. Для таких случаев очень хорошо подходит использование Optional, т.е. нужно не бросать исключения EntityNotFoundException в ActionService вообще, а обрабатывать ошибки и генерировать специальные исключения сразу по месту их возникновения. Например, код может выглядеть как-то так

    // ...
    @Service
    public class ActionService {
        // ...
        public Optional<Action> findByName(String name) {
            return actionRepository.findByName(name);
        }
        // ...
    }

    // ...
    @RestController
    @AllArgsConstructor
    public class ChatController {
        private final ActionService actionService
        // ...
        @PostMapping
        public void create(String name) {
            Action action = actionService
                                .findByName(name)
                                .orElseThrow(() -> new /*...*/); // Специальное исключение
            // ...
        }
        // ...
    }
    // ...
→ Ссылка