在带弹簧休息的全局异常处理程序中使用通用异常类处理程序是一种好习惯吗?

Posted

技术标签:

【中文标题】在带弹簧休息的全局异常处理程序中使用通用异常类处理程序是一种好习惯吗?【英文标题】:Is it good practice to have Generic Exception class handler in global exception handler with spring rest? 【发布时间】:2019-02-22 20:36:30 【问题描述】:

我参考了几篇文章来使用 @ControllerAdvice 为我使用 spring 的 rest api 项目创建全局异常处理程序。这样做的目的是在发生异常的情况下向客户端发送正确格式的响应。在一些文章中,他们在全局异常处理程序中添加了ThrowableException。 我应该用RunTimeException 替换它,因为这个块是为了在运行时发生异常?

异常处理代码:

@ControllerAdvice
public class GlobalExceptionHandler

    @ExceptionHandler(NoDataFoundException.class)
    @ResponseStatus(code=HttpStatus.NOT_FOUND)
    public ResponseEntity<ErrorResponse> handle(NoDataFoundException ex)
        ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
        ResponseEntity<ErrorResponse> response = new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
        return response;
    
    ..... more methods to handle custom exceptions

    @ExceptionHandler(Exception.class)
    @ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity<ErrorResponse> handle(Exception ex)
        ErrorResponse errorResponse = new ErrorResponse("Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR.value());
        ResponseEntity<ErrorResponse> response = new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
        return response;
    

错误响应代码:

public class ErrorResponse 

    private String message;
    private int statusCode;

    public ErrorResponse(String message, int statusCode) 
        super();
        this.message = message;
        this.statusCode = statusCode;
    
    public String getMessage() 
        return message;
    
    public void setMessage(String message) 
        this.message = message;
    
    public int getStatusCode() 
        return statusCode;
    
    public void setStatusCode(int statusCode) 
        this.statusCode = statusCode;
    
  

参考资料:

    https://dzone.com/articles/exception-handling-in-spring-boot-rest-web-service https://github.com/in28minutes/spring-boot-examples/tree/master/spring-boot-2-rest-service-exception-handling

【问题讨论】:

【参考方案1】:

我应该用 RunTimeException 替换它,因为这个块是 运行时发生异常?

为确保您捕获任何抛出的异常,并且从未被您的组件或任何类型比Exception 更多类型异常的异常处理程序处理,您应该有一个Exception 的处理程序。RuntimeException 的处理程序是不够的,因为在运行时也会抛出已检查的异常,并且如果您的高级组件的方法声明指定 throws Exceptionthrows "any checked exception",则可以将已检查的异常传播到客户端或此处的容器将应用默认行为。 例如,想象一下这个可能导致这种情况发生的休息控制器方法声明:

@RequestMapping(value = "/id", method = RequestMethod.GET)
public ResponseEntity<Foo> getOne(@PathVariable long id) throws Exception 
       // ....           

要覆盖这个默认的 Spring 行为,您需要为 Exception 添加一个处理程序。 当然,这并不意味着只为Exception 声明处理程序是一种方式,但是您可能会遇到一些没有特定处理的异常,因此通用处理就可以了。

【讨论】:

我不会遇到控制器抛出异常的情况。我在服务层处理了已检查的异常并抛出了一些未经检查的异常。 这是一件好事,但您认为您在应用程序的整个生命周期内都能保证这种设计吗?不确定。 其他示例。假设您的应用程序(本身或库)使用反射通过使用 Class.newInstance() 来实例化对象,这可能会在未声明的情况下引发检查异常。使用RuntimeException 处理程序可能不会捕获异常。一般而言,考虑让您的应用在现在、不可预知的情况和未来变得健壮。【参考方案2】:

老实说,拥有一个异常处理程序句柄 Exception 对我来说似乎有点懒惰。由于检查了Exception,您应该负责处理错误或从错误中恢复。如果您无法从错误中恢复,或者您所处的环境阻止您编写允许您优雅恢复的代码,则应将其重新抛出 RuntimeException指出问题。

当然,异常处理程序有两个目的:

它们使您能够定义错误响应的外观及其详细内容的标准。 它们使您能够记录错误,以便您稍后返回并修复它。

我强烈鼓励将选中的Exceptions 重新抛出为未选中的模式,并在异常处理程序中处理这些模式。作为万能的故障保护措施,您可以使用Exception 的通用异常处理程序来捕获所有未转换的点,并记录发生的情况。

应该绝不作为开发人员允许Exception 在没有明确原因的情况下一直传播到顶部。

【讨论】:

以上是关于在带弹簧休息的全局异常处理程序中使用通用异常类处理程序是一种好习惯吗?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Gateway 全局通用异常处理

Spring Cloud Gateway 全局通用异常处理

重学SpringBoot系列之统一全局异常处理

springboot中添加全局异常捕获类

springboot自定义异常和全局异常处理

休息服务抛出异常:最好的处理方式