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 шт):
Оборачивание в 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 /*...*/); // Специальное исключение
// ...
}
// ...
}
// ...