«1. Обзор

В этом кратком руководстве мы обсудим новый класс ResponseStatusException, представленный в Spring 5. Этот класс поддерживает применение кодов состояния HTTP к ответам HTTP.

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

2. ResponseStatus

Прежде чем мы углубимся в ResponseStatusException, давайте быстро взглянем на аннотацию @ResponseStatus. Эта аннотация была введена в Spring 3 для применения кода состояния HTTP к ответу HTTP.

Мы можем использовать аннотацию @ResponseStatus, чтобы установить статус и причину в нашем HTTP-ответе:

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Actor Not Found")
public class ActorNotFoundException extends Exception {
    // ...
}

Если это исключение выдается при обработке HTTP-запроса, тогда ответ будет включать в себя HTTP-статус, указанный в этой аннотации. .

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

3. ResponseStatusException

ResponseStatusException — это программная альтернатива @ResponseStatus и базовый класс для исключений, используемых для применения кода состояния к ответу HTTP. Это исключение RuntimeException, поэтому его не требуется явно добавлять в сигнатуру метода.

Spring предоставляет 3 конструктора для генерации ResponseStatusException:

ResponseStatusException(HttpStatus status)
ResponseStatusException(HttpStatus status, java.lang.String reason)
ResponseStatusException(
  HttpStatus status, 
  java.lang.String reason, 
  java.lang.Throwable cause
)

ResponseStatusException, аргументы конструктора:

    статус – статус HTTP, установленный для причины ответа HTTP – сообщение, объясняющее исключение, установленное для HTTP причина ответа — Throwable причина ResponseStatusException

Примечание: в Spring HandlerExceptionResolver перехватывает и обрабатывает любое исключение, сгенерированное и не обработанное контроллером.

Один из этих обработчиков, ResponseStatusExceptionResolver, ищет любые ResponseStatusException или необработанные исключения, аннотированные @ResponseStatus, а затем извлекает код и причину состояния HTTP и включает их в ответ HTTP.

3.1. Преимущества ResponseStatusException

Использование ResponseStatusException имеет несколько преимуществ:

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

4. Примеры

4.1. Generate ResponseStatusException

Теперь давайте посмотрим на пример, который генерирует ResponseStatusException :

@GetMapping("/actor/{id}")
public String getActorName(@PathVariable("id") int id) {
    try {
        return actorService.getActor(id);
    } catch (ActorNotFoundException ex) {
        throw new ResponseStatusException(
          HttpStatus.NOT_FOUND, "Actor Not Found", ex);
    }
}

Spring Boot предоставляет сопоставление по умолчанию / error, возвращая ответ JSON со статусом HTTP.

Вот как выглядит ответ:

$ curl -i -s -X GET http://localhost:8081/actor/8
HTTP/1.1 404
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 26 Dec 2020 19:38:09 GMT

{
    "timestamp": "2020-12-26T19:38:09.426+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "",
    "path": "/actor/8"
}

Начиная с версии 2.3, Spring Boot не включает сообщение об ошибке на странице ошибки по умолчанию. Причина в том, чтобы уменьшить риск утечки информации клиенту

Чтобы изменить поведение по умолчанию, мы можем использовать свойство server.error.include-message.

Давайте установим всегда и посмотрим, что произойдет:

$ curl -i -s -X GET http://localhost:8081/actor/8
HTTP/1.1 404
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 26 Dec 2020 19:39:11 GMT

{
    "timestamp": "2020-12-26T19:39:11.426+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "Actor Not Found",
    "path": "/actor/8"
}

Как мы видим, на этот раз ответ содержит сообщение об ошибке «Актер не найден».

4.2. Другой код состояния — тот же тип исключения

Теперь давайте посмотрим, как другой код состояния устанавливается для ответа HTTP при возникновении исключения того же типа:

@PutMapping("/actor/{id}/{name}")
public String updateActorName(
  @PathVariable("id") int id, 
  @PathVariable("name") String name) {
 
    try {
        return actorService.updateActor(id, name);
    } catch (ActorNotFoundException ex) {
        throw new ResponseStatusException(
          HttpStatus.BAD_REQUEST, "Provide correct Actor Id", ex);
    }
}

Вот как выглядит ответ:

$ curl -i -s -X PUT http://localhost:8081/actor/8/BradPitt
HTTP/1.1 400
...
{
    "timestamp": "2018-02-01T04:28:32.917+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "Provide correct Actor Id",
    "path": "/actor/8/BradPitt"
}

5. Заключение

В этом кратком руководстве мы обсудили, как создать исключение ResponseStatusException в нашей программе.

Мы также подчеркнули, что программно это лучший способ установить коды состояния HTTP в ответе HTTP, чем аннотация @ResponseStatus.

Как всегда, полный исходный код доступен на GitHub.