Spring Boot 默认异常映射到标准 HTTP 状态码

Posted

技术标签:

【中文标题】Spring Boot 默认异常映射到标准 HTTP 状态码【英文标题】:Spring Boot default exceptions mapped to standard HTTP status codes 【发布时间】:2018-05-09 11:10:16 【问题描述】:

我知道可以在 Spring Boot 中定义自定义异常处理程序并具有例如IllegalArgumentException 异常映射到 HTTP 400 错误请求。我想知道 Spring Web/Boot 中定义的任何现有异常是否映射到标准 HTTP 状态代码?这样我就可以扔掉它们,它们会自动映射到标准的 HTTP 状态代码。

【问题讨论】:

【参考方案1】:

默认情况下,ResponseEntityExceptionHandler 将有效地将 Spring 内部抛出的异常转换为 HTTP 状态代码。但是,将异常转换为 HTTP 状态代码不会提供有关异常的任何重要日志。良好的安全实践要求外部发送的错误消息应该是关于系统内部的信息最少的。相反,日志应尽可能提供信息。

此外,ResponseEntityExceptionHandler 只处理 Spring 生成的异常。所有与业务相关的异常都必须单独处理。例如,这个类不处理从 findSomething(xxx) 方法抛出的“找不到记录”异常。

以下是如何解决这些缺点的示例:

Spring 引发内部错误 您必须覆盖感兴趣的异常的处理程序,并提供要返回给调用者的内部日志消息和外部错误消息。

@ControllerAdvice 是一个注释,它使用声明 @ExceptionHandler 注释方法的类来包装 @Component 类。简单地说,这些处理程序将包装所有@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 类,并使用 @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 注释的类,该类将包含所有这些异常处理方法。由于对这些 @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());
    
   ....

最后,辅助类 LogError 和 HttpErrorResponse 是它们各自目的地的简单格式化程序。

希望这会有所帮助。

杰克

【讨论】:

【参考方案2】:

有一些,例如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

【讨论】:

无 409 冲突;c

以上是关于Spring Boot 默认异常映射到标准 HTTP 状态码的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot中Controller的统一异常处理

基于spring boot的统一异常处理

我的 Spring Boot 项目有映射异常

spring boot 1.5.4 统一异常处理

Spring Boot GCP:将 PubSub 应用程序部署到 App Engine 标准环境时出现“Google 凭据”异常

spring-boot实战07:Spring Boot中Web应用的统一异常处理