rest - 自定义 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
而不是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
,并且知道要响应哪个状态代码。
有没有更清洁的方法来处理这个?
解决方案
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
推荐阅读
- twitter-bootstrap - 视差左侧编号仅在活动动画线部分显示。一次只能看到一个数字
- laravel-5 - Laravel如何使用Postgres SQL获取数据随机行?
- java - OSX - 从 /Library/Application Support/TestAPI 中删除文件夹
- objective-c - 使 Xcode 在向数组或字典添加可空属性时发出警告
- machine-learning - 从非结构化 pdf 分析中提取数据,我应该通过哪些工具和库?
- asp.net-core - 在 Linux 上使用 NodaTime 会丢弃使用 DateTimeOffset 转换为本地时间的时区信息
- django - 如何从 html 页面重定向以在 Django 中查看和发布数据
- verilog - Verilog 中的 Always Block 每次都会执行
- javascript - 这是使用值替换更新输入类型 = 数字字段的预期行为
- json - 如何在 Postman App 中自动加载 Postman Collection 文件