首页 > 解决方案 > 自定义 Spring Boot 异常处理以防止在 Rest 响应中返回 Stacktraces

问题描述

如何配置我的 Spring Boot 服务,以便 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"
}

注意:这里的堆栈跟踪位于 json 中,message而不是exceptionjson 的一部分。

如您所见,我已经在格式化时间戳:

@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,并且知道要响应哪个状态代码。

有没有更清洁的方法来处理这个?

标签: restspring-bootexceptionerror-handling

解决方案


Spring-boot为我们提供了一种使用 Spring AOP 概念处理异常的标准方法。您可以使用@ControllerAdvice@Exceptionhandled注释来处理来自 spring-boot 休息端点的异常,以便始终从带有正确错误代码和错误响应的休息端点抛出自定义异常。

@ResponseStatus()注释可用于自定义抛出的响应代码。例如考虑自定义异常:

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

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

}

当找不到数据时,我们可以从 rest 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


推荐阅读