Spring:RestController 和 Controller 的不同异常处理程序

在春季,您可以通过@ControllerAdvice和@ExceptionHandler注释来设置“全局”异常处理程序。我正在尝试利用此机制来拥有两个全局异常处理程序:

  • RestControllerExceptionHandler- 它应该将错误响应作为json返回任何带有注释的控制器@RestController
  • ControllerExceptionHandler- 应将错误消息打印到任何其他控制器的屏幕上(标有@Controller)

问题是,当我声明这两个异常处理程序时,spring总是使用 和 从不来处理异常。ControllerExceptionHandlerRestControllerExceptionHandler

如何使它工作?BTW:我试图使用@Order注释,但这似乎不起作用。

以下是我的异常处理程序:

// should handle all exception for classes annotated with         
@ControllerAdvice(annotations = RestController.class)
public class RestControllerExceptionHandler {


  @ExceptionHandler(Exception.class)
  public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {

    // below object should be serialized to json
    ErrorResponse errorResponse = new ErrorResponse("asdasd"); 

    return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
  }

// should handle exceptions for all the other controllers
@ControllerAdvice(annotations = Controller.class)
public class ControllerExceptionHandler {


  @ExceptionHandler(Exception.class)
  public ResponseEntity<String> handleUnexpectedException(Exception e) {
    return new ResponseEntity<String>("Unexpected exception, HttpStatus.INTERNAL_SERVER_ERROR);
  }


}
}

当我删除比被春天正确调用时(仅适用于标有)....但是当我添加比所有通过.为什么?ControllerExceptionHandlerRestControllerExceptionHandler@RestControllerControllerExceptionHandlerControllerExceptionHandler


答案 1

经过一些更深入的调查,似乎字母顺序很重要:/。

当我将 my 重命名为(按字母顺序排列在前面)时,一切都按预期工作! 正确处理来自其他控制器的异常并处理来自其他控制器的异常。RestControllerExceptionHandlerARestControllerExceptionHandlerControllerExceptionHandlerARestControllerExceptionHandlerRestControllersControllerExceptionHandler

我在春天为此创建了一个错误:https://jira.spring.io/browse/SPR-15432

--- 编辑:

我收到了SPR-15432的答案,其中建议可以通过()注释或实现有序接口来解决这种情况。@Orderorg.springframework.core.annotation.Order

这在以前对我不起作用,但似乎我导入了错误的@Order注释(来自log4j2而不是spring)。修复此问题后,它可以正常工作。固定版本如下:

// should handle all exception for classes annotated with         
@ControllerAdvice(annotations = RestController.class)
@Order(1) // NOTE: order 1 here
public class RestControllerExceptionHandler {


  @ExceptionHandler(Exception.class)
  public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {

    // below object should be serialized to json
    ErrorResponse errorResponse = new ErrorResponse("asdasd"); 

    return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}
// should handle exceptions for all the other controllers
@ControllerAdvice(annotations = Controller.class)
@Order(2)  // NOTE: order 2 here
public class ControllerExceptionHandler {


  @ExceptionHandler(Exception.class)
  public ResponseEntity<String> handleUnexpectedException(Exception e) {
    return new ResponseEntity<String>("Unexpected exception, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

答案 2

之所以发生这种情况,是因为@RestController注释本身也是一个@Controller因此Spring正在考虑对两者进行@ControllerAdvice。annotation = Controller.class

您可以尝试使用另一种方法来定义@ControllerAdvice将生效的控制器子集,因此以下是一些解决方案:


解决方案 1

  1. 创建新的批注@NotRestController:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
        public @interface NotRestController {
    }
    
  2. 标记未同时使用@Controller和@NotRestController@RestController控制器:

    @Controller
    @NotRestController
    @RequestMapping("/controller")
    public class SampleController {
    }
    
  3. 用于 :NotRestController.classControllerExceptionHandler

    @ControllerAdvice(annotations = NotRestController.class)
    public class ControllerExceptionHandler {
    

我创建了一个示例项目,并在Github上共享它:Solution 1

https://github.com/brunocleite/springexceptionhandlertest


解决方案 2

  1. 将@RestController类移动到一个包中,将@Controller类移动到另一个包中。foo.barfoo.nuk

  2. 使用 的属性代替 :basePackages@ControllerAdviceannotations

    @ControllerAdvice(basePackages={"foo.bar"})
    public class RestControllerExceptionHandler {
    
    @ControllerAdvice(basePackages={"foo.nuk"})
    public class ControllerExceptionHandler {
    

此致敬意!


推荐