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

wal*_*ros 5 java rest spring exception-handling spring-mvc

在Spring中,您可以通过@ControllerAdvice和@ExceptionHandler注释设置"全局"异常处理程序.我正在尝试利用这种机制来拥有两个全局异常处理程序:

  • RestControllerExceptionHandler - 对于任何带注释的控制器,它应该以json的形式返回错误响应 @RestController
  • ControllerExceptionHandler- 应该在屏幕上为任何其他控制器打印错误消息(附带有@Controller)

问题是,当我声明这两个异常处理程序时,spring总是使用ControllerExceptionHandler和永远不会RestControllerExceptionHandler处理异常.

如何使这项工作?顺便说一句:我试图使用@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);
  }


}
}
Run Code Online (Sandbox Code Playgroud)

当我删除ControllerExceptionHandlerRestControllerExceptionHandler春天正确调用时(仅适用于带注释的类@RestController)....但是当我添加时ControllerExceptionHandler,所有通过ControllerExceptionHandler.为什么?

wal*_*ros 9

经过深入调查后,字母顺序似乎很重要:/.

当我将我重命名RestControllerExceptionHandlerARestControllerExceptionHandler(按字母顺序排在前面ControllerExceptionHandler)时,所有工作都按预期进行!ARestControllerExceptionHandler正确处理异常RestControllersControllerExceptionHandler处理来自其他控制器的异常.

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

---编辑:

我收到了SPR-15432的答案,建议可以使用@Order(org.springframework.core.annotation.Order)注释或通过实现Ordered接口来解决这种情况.

这对我来说不起作用,但似乎我导入了错误的@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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Bru*_*ite 8

这种情况正在发生,因为@RestController注释本身也是一个@Controller,因此Spring正在考虑使用@ControllerAdvice annotation = Controller.class.

你可以尝试另一种方法来定义@ControllerAdvice将有效的控制器子集,所以这里有一些解决方案:


解决方案1

  1. 创建一个新的注释@NotRestController:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
        public @interface NotRestController {
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用@Controller和@NotRestController标记不是@RestController的控制器:

    @Controller
    @NotRestController
    @RequestMapping("/controller")
    public class SampleController {
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用NotRestController.classControllerExceptionHandler:

    @ControllerAdvice(annotations = NotRestController.class)
    public class ControllerExceptionHandler {
    
    Run Code Online (Sandbox Code Playgroud)

Solution 1在Github上创建了一个示例项目并将其共享:

https://github.com/brunocleite/springexceptionhandlertest


解决方案2

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

  2. 使用以下basePackages属性@ControllerAdvice代替annotations:

    @ControllerAdvice(basePackages={"foo.bar"})
    public class RestControllerExceptionHandler {
    
    @ControllerAdvice(basePackages={"foo.nuk"})
    public class ControllerExceptionHandler {
    
    Run Code Online (Sandbox Code Playgroud)

最好的祝福!

  • 但是,我知道@Controller建议的其他选项(如解决方案1中所述),但我不想使用它们,因为我想利用现有的RestController anotation.解决方案1没问题,但是我不希望添加额外的不必要的注释. (2认同)