Настройка обработки исключений Spring Boot для предотвращения возврата стековых трасс в ответе покоя - PullRequest
0 голосов
/ 01 мая 2019

Как настроить службу весенней загрузки таким образом, чтобы ошибки, такие как 500, потенциально не пропускали детали реализации, такие как трассировки стека.

{
  "timestamp": "2019/05/01 15:06:17",
  "status": 500,
  "error": "Internal Server Error",
  "message": "Type definition error: [simple type, class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.Collections$UnmodifiableRandomAccessList[0]->........)",
  "path": "/api/test"
}

Примечание: здесь трассировка стека находится в message, а не в exception части json.

Как видите, я уже форматирую отметку времени с помощью:

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {

  private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
  private static final String TIMESTAMP = "timestamp";

  @Override
  public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {

    //Let Spring handle the error first
    Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);

    //Format & update timestamp
    Object timestamp = errorAttributes.get(TIMESTAMP);
    if(timestamp == null) {
      errorAttributes.put(TIMESTAMP, dateFormat.format(new Date()));
    } else {
      errorAttributes.put(TIMESTAMP, dateFormat.format((Date)timestamp));
    }

    return errorAttributes;
  }
}

Но мне тоже нужно обработать сообщение.

Если бы эти 500 были единственной ошибкой, которую я мог бы просто сделать:

errorAttributes.put("message", "Server error. Contact support.");

Однако все ошибки здесь проходят, и это переопределяет все сообщения.

Я мог бы проверить, если статус 500 и изменить его только тогда. Однако могут быть и другие ошибки, которые также могут привести к утечке стековых трасс.

Использование @RestControllerAdvice, по-видимому, требует знания каждого сгенерированного исключения и наличия @ExceptionHandler для каждого и знания того, с каким кодом состояния отвечать.

Есть ли более чистый способ справиться с этим?

Ответы [ 2 ]

0 голосов
/ 17 мая 2019

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

The @ResponseStatus() аннотация может использоваться для настройки создаваемого кода ответа.Например, рассмотрим пользовательское исключение:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class DataNotFoundException extends RuntimeException {

  public DataNotFoundException(String exception) {
    super(exception);
  }

}

Мы можем выбросить эту ошибку из сопоставления GET покоя, когда данные не найдены, например:

  @GetMapping("/trains/{id}")
  public Resource<Student> retrieveTrains(@PathVariable long id) {
    Optional<Trains> trains = trainRepository.findById(id);

    if (!train.isPresent())
      throw new DataNotFoundException("id-" + id);

    Resource<Trains> resource = new Resource<Trains>(train.get());

    ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllTrains());

    resource.add(linkTo.withRel("all-trains"));

    return resource;
  }

Ответ по умолчанию об ошибке , предоставляемый Spring Boot, содержит все детали, которые обычно необходимы.

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

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

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

Чтобы использовать этот узел ошибки, мы используем:

@ControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(DataNotFoundException.class)
  public final ResponseEntity<ErrorDetails> handleUserNotFoundException(DataNotFoundException ex, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
        request.getDescription(false));
    return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
  }
  • @ExceptionHandler(DataNotFoundException.class) указывает, что этот метод будет обрабатывать исключения определенного типа.
  • new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND) - Создать объект ответа об ошибке и вернуть его с определенным статусом Http.

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

Как:

@ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorDetails> handleAllExceptions(Exception ex, WebRequest request) {
  ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
      request.getDescription(false));
  return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}

Ссылка от: https://www.javaguides.net/2019/02/spring-boot-2-angular-7-crud-example-tutorial.html

0 голосов
/ 02 мая 2019

Возможно, это не самый «чистый» подход, но в проектах, в которых я участвовал, у нас был «стандартный формат» для наших ответов на ошибки в разных проектах, поэтому у нас был собственный объект с полями, которые соответствуют нашему стандарту orgs (HttpStatus, Reason, ect.), Который расширил исключение RuntimeException.Затем в наших контроллерах, сервисах, репозиториях и т. Д. Мы будем перехватывать исключения и создавать этот объект соответствующим образом, а вместо этого выбрасывать пользовательский.Исходя из того, где это произошло в приложении (репозиторий, сервис, контроллер и т. Д.), Мы могли бы добавить к нему собственную настраиваемую деталь, но при этом регистрировать полное исключение в журналах нашего сервера, чтобы мы могли исследовать его позже

Например, если мы поймаем ошибку в нашем репозитории, мы создадим наш собственный объект ошибки, установим для Reason значение DB, недоступное (на самом деле все, что нужно знать потребителю), зададим статус HttpStatus.SERVICE_UNAVAILABLE (мы отслеживали их с причинами, а httpstatus - сперечисляет, чтобы сохранить статус одинаковым для всех модулей), и выбрасывать пользовательский объект до контроллера, который должен быть возвращен.

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

Пользовательское исключение:

data class MyException(
    val reason: String,
    val httpStatus: HttpStatus? = null
) : RuntimeException(reason)

Метод создания:

fun createApiException(errorCode: ErrorCodeEnum) = MyException(
    reason = errorCode.reason,
    httpStatus = errorCode.httpStatus,
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...