Spring Boot 异常自定义处理 - 意外的 HTTP 状态

Posted

技术标签:

【中文标题】Spring Boot 异常自定义处理 - 意外的 HTTP 状态【英文标题】:Spring boot Exception custom handling - Unexpected HTTP status 【发布时间】:2019-02-25 02:14:41 【问题描述】:

我正在尝试在我的 Spring Boot 应用程序中实现一些自定义异常处理程序,这些处理程序将能够处理自定义异常并显示适当的消息和状态代码。

我的问题:即使响应正文是根据我的自定义处理程序获得的 http 状态 = 500。

代码:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler 

@ExceptionHandler( BadRequestValidationFailureException.class, Exception.class )
public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) 

    ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage());

    return new ResponseEntity<Object>( apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST );


并抛出异常:

throw new BadRequestValidationFailureException( "ERROR_CODE", "THIS IS THE MESSAGE" );

输出是:


"timestamp": "2018-09-20T17:44:01.502Z",
"status": 500,
"error": "Internal Server Error",
"exception": "com.hotstar.payment.exception.BadRequestValidationFailureException",
"message": "[ ERROR_CODE ] THIS IS THE MESSAGE",
"path": "/my/api/path"

奇怪的是http响应状态是500。

请帮忙。

【问题讨论】:

【参考方案1】:

找到解决方案。不得不设置另一个注释:

@ResponseStatus(value = HttpStatus.BAD_REQUEST, code = HttpStatus.BAD_REQUEST, reason = "some reason")

将此添加到handleAll 方法。

【讨论】:

【参考方案2】:

我在以下模式方面取得了很好的经验:

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler 
    private static final Map<MyProjectErrorCode, HttpStatus> CODE_STATUS_MAP = new EnumMap<>(MyProjectErrorCode.class);

    static 
        CODE_STATUS_MAP.put(MyProjectErrorCode.MYPROJ_ILLEGAL_PROPERTY, HttpStatus.BAD_REQUEST);
        CODE_STATUS_MAP.put(MyProjectErrorCode.MYPROJ_FOO, HttpStatus.BAD_REQUEST);
        CODE_STATUS_MAP.put(MyProjectErrorCode.MYPROJ_THIRDPARTYX_CLIENT, HttpStatus.INTERNAL_SERVER_ERROR);
        CODE_STATUS_MAP.put(MyProjectErrorCode.MYPROJ_UNKNOWN, HttpStatus.INTERNAL_SERVER_ERROR);
        CODE_STATUS_MAP.put(MyProjectErrorCode.THIRDPARTYX_BAR, HttpStatus.BAD_REQUEST);
        CODE_STATUS_MAP.put(MyProjectErrorCode.THIRDPARTYX_UNKNOWN, HttpStatus.INTERNAL_SERVER_ERROR);
    

    @ExceptionHandler(MyProjectException.class)
    public ResponseEntity<ErrorResponse> handleMyProjectException(MyProjectException ex) 
        ErrorResponse errorResponse = createErrorResponse(ex.getErrorCode(), ex.getMessage());
        HttpStatus httpStatus = determineHttpStatus(ex.getErrorCode());
        return handleErrorResponse(errorResponse, httpStatus);
    

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) 
        MyProjectErrorCode errorCode = MyProjectErrorCode.MYPROJ_ILLEGAL_PROPERTY;
        ErrorResponse errorResponse = createErrorResponse(errorCode, ex.getMessage());
        HttpStatus httpStatus = determineHttpStatus(errorCode);
        return handleErrorResponse(errorResponse, httpStatus);
    

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException ex) 
        MyProjectErrorCode errorCode = MyProjectErrorCode.MYPROJ_UNKNOWN;
        ErrorResponse errorResponse = createErrorResponse(errorCode, ex.getMessage());
        HttpStatus httpStatus = determineHttpStatus(errorCode);
        return handleErrorResponse(errorResponse, httpStatus);
    

    private ResponseEntity<ErrorResponse> handleErrorResponse(ErrorResponse errorResponse, HttpStatus httpStatus) 
        return new ResponseEntity<>(errorResponse, httpStatus);
    

    private ErrorResponse createErrorResponse(MyProjectErrorCode errorCode, String message) 
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorCode(errorCode.name());
        errorResponse.setErrorMessage(message);
        return errorResponse;
    

    private HttpStatus determineHttpStatus(MyProjectErrorCode errorCode) 
        return CODE_STATUS_MAP.getOrDefault(errorCode, HttpStatus.INTERNAL_SERVER_ERROR);

客户端可以从 Http 响应中获取 HttpStatus - 无需将其添加到 JSON 正文中。

项目特定的 MyProjectErrorCode 枚举允许您向客户传达详细的错误代码。客户端可以分析此错误代码并根据错误代码采取适当的措施或显示本地化(特定或通用)错误消息。

MyProjectErrorCode 还允许您在代码中创建错误(以 MYPROJ_ 开头)或错误是否从第三方“x”服务转发(以 THIRDPARTYX_ 开头)进行通信。

您还可以创建 MyProjectException 和 ErrorResponse 的子类来为特定情况传输更具体的数据 - 只需为该异常添加一个额外的异常处理方法。

【讨论】:

以上是关于Spring Boot 异常自定义处理 - 意外的 HTTP 状态的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 中关于自定义异常处理的套路!

Spring Boot - 使用 RestControllerAdvice 的全局自定义异常处理机制

处理自定义转换器抛出的 Spring Boot REST 异常

Spring Boot 从异常处理程序返回 401 状态自定义对象

缺少资源 bean:Spring Boot webflux 自定义全局异常处理程序

Spring boot异常统一处理方法:@ControllerAdvice注解的使用全局异常捕获自定义异常捕获