Spring 引导默认异常映射到标准 HTTP 状态代码
Spring Boot default exceptions mapped to standard HTTP status codes
我知道可以在 Spring Boot 中定义自定义异常处理程序,例如IllegalArgumentException
异常映射到 HTTP 400 错误请求。我想知道 Spring Web/Boot 中定义的任何现有异常是否映射到标准 HTTP 状态代码?这样我就可以抛出它们,它们将自动映射到标准的 HTTP 状态代码。
有一些例如HttpRequestMethodNotSupportedException
映射到 405。
查看 ResponseEntityExceptionHandler.handleException()
方法,它定义了 Spring MVC 中处理常见异常的基本规则。你会发现
NoSuchRequestHandlingMethodException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
实际上,默认情况下,ResponseEntityExceptionHandler 会将 Spring 内部抛出的异常转换为 HTTP 状态代码。但是,将异常转换为 HTTP 状态代码不会提供有关异常的任何重要日志。良好的安全实践规定,外部发送的错误消息应该尽可能少地提供有关系统内部的信息。相反,日志应尽可能提供信息。
此外,ResponseEntityExceptionHandler 仅处理 Spring 生成的异常。所有与业务相关的异常都必须单独处理。例如,从 findSomething(xxx) 方法抛出的 "Record not found" 异常不会被此 class 处理。
以下是有关如何解决这些缺点的示例:
Spring 抛出内部错误
您必须覆盖感兴趣的异常的处理程序,并提供要返回给调用者的内部日志消息和外部错误消息。
@ControllerAdvice 是一个注释,它用 classes 包装 @Component classes 声明 @ExceptionHandler 注释方法。简单地说,这些处理程序将包装所有@Component 方法。
@Slf4j
@ControllerAdvice
public class InternalExceptionHandler extends ResponseEntityExceptionHandler {
@Override
public ResponseEntity<Object> handleMissingServletRequestParameter(
MissingServletRequestParameterException e,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
LogError error = new LogError("MissingServletRequestParameterException",
HttpStatus.BAD_REQUEST,
String.format("Missing '%s' parameter", e.getParameterName()));
log.debug(error.toJson());
HttpErrorResponse response = new HttpErrorResponse(error.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
业务层抛出错误
您必须首先为每个异常创建一个特定的 RuntimeException class 并用 @ResponseStatus 对其进行注释。
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
然后,您创建一个带注释的@ControllerAdvice class,它将包含所有这些异常处理程序方法。没有 class 派生,因为这些 @ExceptionHandler 注释方法的内部重定向由 Spring.
管理
@Slf4j
@ControllerAdvice
public class ClientExceptionHandler {
@ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
最后,帮助程序 classes LogError 和 HttpErrorResponse 是各自目标的简单格式化程序。
希望对您有所帮助。
杰克
我知道可以在 Spring Boot 中定义自定义异常处理程序,例如IllegalArgumentException
异常映射到 HTTP 400 错误请求。我想知道 Spring Web/Boot 中定义的任何现有异常是否映射到标准 HTTP 状态代码?这样我就可以抛出它们,它们将自动映射到标准的 HTTP 状态代码。
有一些例如HttpRequestMethodNotSupportedException
映射到 405。
查看 ResponseEntityExceptionHandler.handleException()
方法,它定义了 Spring MVC 中处理常见异常的基本规则。你会发现
NoSuchRequestHandlingMethodException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
实际上,默认情况下,ResponseEntityExceptionHandler 会将 Spring 内部抛出的异常转换为 HTTP 状态代码。但是,将异常转换为 HTTP 状态代码不会提供有关异常的任何重要日志。良好的安全实践规定,外部发送的错误消息应该尽可能少地提供有关系统内部的信息。相反,日志应尽可能提供信息。
此外,ResponseEntityExceptionHandler 仅处理 Spring 生成的异常。所有与业务相关的异常都必须单独处理。例如,从 findSomething(xxx) 方法抛出的 "Record not found" 异常不会被此 class 处理。
以下是有关如何解决这些缺点的示例:
Spring 抛出内部错误 您必须覆盖感兴趣的异常的处理程序,并提供要返回给调用者的内部日志消息和外部错误消息。
@ControllerAdvice 是一个注释,它用 classes 包装 @Component classes 声明 @ExceptionHandler 注释方法。简单地说,这些处理程序将包装所有@Component 方法。
@Slf4j
@ControllerAdvice
public class InternalExceptionHandler extends ResponseEntityExceptionHandler {
@Override
public ResponseEntity<Object> handleMissingServletRequestParameter(
MissingServletRequestParameterException e,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
LogError error = new LogError("MissingServletRequestParameterException",
HttpStatus.BAD_REQUEST,
String.format("Missing '%s' parameter", e.getParameterName()));
log.debug(error.toJson());
HttpErrorResponse response = new HttpErrorResponse(error.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
业务层抛出错误
您必须首先为每个异常创建一个特定的 RuntimeException class 并用 @ResponseStatus 对其进行注释。
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
然后,您创建一个带注释的@ControllerAdvice class,它将包含所有这些异常处理程序方法。没有 class 派生,因为这些 @ExceptionHandler 注释方法的内部重定向由 Spring.
管理@Slf4j
@ControllerAdvice
public class ClientExceptionHandler {
@ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
最后,帮助程序 classes LogError 和 HttpErrorResponse 是各自目标的简单格式化程序。
希望对您有所帮助。
杰克