@ControllerAdvice 和@ExceptionHandler 没有被我的 RestController 触发
@ControllerAdvice and @ExceptionHandler not getting triggered for my RestController
为了在整个应用程序中进行统一的异常处理,我使用 Error Handling for REST with Spring 解决方案#3,它使用 @ControllerAdvice
和 @ExceptionHandler
。
Spring版本:4.3.22.RELEASE
Spring 引导版本:1.5.19.RELEASE
这是一个 Spring 引导应用程序,以下是我的包结构。
src/main/java
com.test.app.controller
MyRestController.java -- This is my Rest controller
com.test.app.handler
RestExceptionHandler.java -- This is my ControllerAdvice class
以下是我的 ControllerAdvice 代码,其中一个控制器抛出 InvalidDataException
但仍未调用相应的 @ExceptionHandler
。相反,我得到 Unexpected 'e'
作为响应主体 http 400.
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(InvalidDataException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody ErrorResponse handleValidationError(final InvalidDataException ex,
final WebRequest request) {
log.error("InvalidDataException message:{} ", ex.getMessage());
return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
}
private ErrorResponse getExceptionResponse(final String message, final Integer errorCode) {
final ErrorResponse exceptionResponse = new ErrorResponse();
exceptionResponse.setErrorCode(errorCode.toString());
exceptionResponse.setErrorDescription(message);
log.error("message:{}", exceptionResponse);
return exceptionResponse;
}
}
我查看了 SO 上的其他 post 以及他们提到使用 @EnableWebMvc
和 @ComponentScan
等的其他论坛,但没有任何帮助。有人可以帮我理解我错过了什么吗?
以下是我的Controller
和对应的界面
@RestController
public class MyRestController implements MyApi {
@Override
public ResponseEntity<List<MyResponse>> myGet(@RequestHeader(value = "a") String a,
@RequestHeader(value = "b") String b,
@RequestHeader(value = "c") String c,
@RequestHeader(value = "d") String d,
@RequestHeader(value = "e") String e,
@RequestHeader(value = "f") String f,
@RequestHeader(value = "g") String g) {
List<MyResponse> responses = service.getData(c, d, e, f); // This throws exception
return new ResponseEntity<>(responses, HttpStatus.OK);
}
}
@Validated
@Api(value = "My", description = "the My API")
//This is generated interface through swagger codegen
public interface MyApi {
@ApiOperation(value = "", nickname = "myGet", notes = "", response = MyResponse.class, responseContainer = "List")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "normal response", response = MyResponse.class, responseContainer = "List"),
@ApiResponse(code = 400, message = "Request is invalid", response = ErrorResponse.class),
@ApiResponse(code = 401, message = "", response = ErrorResponse.class),
@ApiResponse(code = 404, message = "", response = ErrorResponse.class),
@ApiResponse(code = 405, message = "", response = ErrorResponse.class),
@ApiResponse(code = 409, message = "", response = ErrorResponse.class),
@ApiResponse(code = 500, message = "Internal Server Error", response = ErrorResponse.class),
@ApiResponse(code = 503, message = "Service Unavailable", response = ErrorResponse.class) })
@RequestMapping(value = "/v1/test",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<List<MyResponse>> myGet(@ApiParam(value = "a" ,required=true) @RequestHeader(value="a", required=true) String a,
@ApiParam(value = "b" ,required=true) @RequestHeader(value="b", required=true) String b,
@ApiParam(value = "c" ,required=true) @RequestHeader(value="c", required=true) String c,
@ApiParam(value = "d" ,required=true) @RequestHeader(value="d", required=true) String d,
@ApiParam(value = "e" ,required=true) @RequestHeader(value="e", required=true) String e,
@ApiParam(value = "f" ,required=true) @RequestHeader(value="f", required=true) String f,
@ApiParam(value = "g" ,required=true) @RequestHeader(value="g", required=true) String g) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "{ \"aNum\" : 0, \"cNum\" : \"cNum\"}");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
以下是我的 GlobalExceptionHandler
中的代码片段
class GlobalExceptionHandler extends ExceptionHandlerExceptionResolver implements HandlerExceptionResolver, Ordered, InitializingBean {
...
@Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
if (exception instanceof com.myframework.SystemException) {
return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.SystemException.class.getName()));
} else if (exception instanceof GenericApplicationException) {
return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.GenericApplicationException.class.getName()));
} else {
return null;
}
}
....
}
确保您没有其他 spring 组件扩展
AbstractErrorController
下面的控制器建议应该可以解决问题。
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = {InvalidDataException.class})
protected ResponseEntity<Object> handleInvalidDataException(
RuntimeException ex, WebRequest request) {
return new ResponseEntity<>(getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
}
应该可以。导致失败的一些可能原因可能是:
RestExceptionHandler
还没有声明为 spring bean 吗? @SpringBootApplication
默认情况下只会扫描 spring bean 以在其包及其所有子包下注册。
控制器实际上没有抛出 InvalidDataException
但其他异常 ?
无论如何,我建议您进行以下更改以检查 RestExceptionHandler
是否被调用。
在您的 spring 引导主应用程序 class 中,使用 @Import
明确地将 RestExceptionHandler
注册为 spring bean
@SpringBootApplication
@Import(RestExceptionHandler.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
同样在 RestExceptionHandler
中,还包含一个捕获最通用异常的方法:
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ErrorResponse handleGenericException(final Exception ex ,final WebRequest request) {
System.out.println("handleGenericException ....");
return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
}
}
请告诉我 RestExceptionHandler
是否会在进行这些更改后被调用。
将@ControllerAdvice
替换为@RestControllerAdvice
为了在整个应用程序中进行统一的异常处理,我使用 Error Handling for REST with Spring 解决方案#3,它使用 @ControllerAdvice
和 @ExceptionHandler
。
Spring版本:4.3.22.RELEASE
Spring 引导版本:1.5.19.RELEASE
这是一个 Spring 引导应用程序,以下是我的包结构。
src/main/java
com.test.app.controller
MyRestController.java -- This is my Rest controller
com.test.app.handler
RestExceptionHandler.java -- This is my ControllerAdvice class
以下是我的 ControllerAdvice 代码,其中一个控制器抛出 InvalidDataException
但仍未调用相应的 @ExceptionHandler
。相反,我得到 Unexpected 'e'
作为响应主体 http 400.
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(InvalidDataException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody ErrorResponse handleValidationError(final InvalidDataException ex,
final WebRequest request) {
log.error("InvalidDataException message:{} ", ex.getMessage());
return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
}
private ErrorResponse getExceptionResponse(final String message, final Integer errorCode) {
final ErrorResponse exceptionResponse = new ErrorResponse();
exceptionResponse.setErrorCode(errorCode.toString());
exceptionResponse.setErrorDescription(message);
log.error("message:{}", exceptionResponse);
return exceptionResponse;
}
}
我查看了 SO 上的其他 post 以及他们提到使用 @EnableWebMvc
和 @ComponentScan
等的其他论坛,但没有任何帮助。有人可以帮我理解我错过了什么吗?
以下是我的Controller
和对应的界面
@RestController
public class MyRestController implements MyApi {
@Override
public ResponseEntity<List<MyResponse>> myGet(@RequestHeader(value = "a") String a,
@RequestHeader(value = "b") String b,
@RequestHeader(value = "c") String c,
@RequestHeader(value = "d") String d,
@RequestHeader(value = "e") String e,
@RequestHeader(value = "f") String f,
@RequestHeader(value = "g") String g) {
List<MyResponse> responses = service.getData(c, d, e, f); // This throws exception
return new ResponseEntity<>(responses, HttpStatus.OK);
}
}
@Validated
@Api(value = "My", description = "the My API")
//This is generated interface through swagger codegen
public interface MyApi {
@ApiOperation(value = "", nickname = "myGet", notes = "", response = MyResponse.class, responseContainer = "List")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "normal response", response = MyResponse.class, responseContainer = "List"),
@ApiResponse(code = 400, message = "Request is invalid", response = ErrorResponse.class),
@ApiResponse(code = 401, message = "", response = ErrorResponse.class),
@ApiResponse(code = 404, message = "", response = ErrorResponse.class),
@ApiResponse(code = 405, message = "", response = ErrorResponse.class),
@ApiResponse(code = 409, message = "", response = ErrorResponse.class),
@ApiResponse(code = 500, message = "Internal Server Error", response = ErrorResponse.class),
@ApiResponse(code = 503, message = "Service Unavailable", response = ErrorResponse.class) })
@RequestMapping(value = "/v1/test",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<List<MyResponse>> myGet(@ApiParam(value = "a" ,required=true) @RequestHeader(value="a", required=true) String a,
@ApiParam(value = "b" ,required=true) @RequestHeader(value="b", required=true) String b,
@ApiParam(value = "c" ,required=true) @RequestHeader(value="c", required=true) String c,
@ApiParam(value = "d" ,required=true) @RequestHeader(value="d", required=true) String d,
@ApiParam(value = "e" ,required=true) @RequestHeader(value="e", required=true) String e,
@ApiParam(value = "f" ,required=true) @RequestHeader(value="f", required=true) String f,
@ApiParam(value = "g" ,required=true) @RequestHeader(value="g", required=true) String g) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "{ \"aNum\" : 0, \"cNum\" : \"cNum\"}");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
以下是我的 GlobalExceptionHandler
class GlobalExceptionHandler extends ExceptionHandlerExceptionResolver implements HandlerExceptionResolver, Ordered, InitializingBean {
...
@Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
if (exception instanceof com.myframework.SystemException) {
return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.SystemException.class.getName()));
} else if (exception instanceof GenericApplicationException) {
return new ServletInvocableHandlerMethod(this, exceptionMethods.get(com.myframework.GenericApplicationException.class.getName()));
} else {
return null;
}
}
....
}
确保您没有其他 spring 组件扩展
AbstractErrorController
下面的控制器建议应该可以解决问题。
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = {InvalidDataException.class})
protected ResponseEntity<Object> handleInvalidDataException(
RuntimeException ex, WebRequest request) {
return new ResponseEntity<>(getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
}
应该可以。导致失败的一些可能原因可能是:
RestExceptionHandler
还没有声明为 spring bean 吗?@SpringBootApplication
默认情况下只会扫描 spring bean 以在其包及其所有子包下注册。控制器实际上没有抛出
InvalidDataException
但其他异常 ?
无论如何,我建议您进行以下更改以检查 RestExceptionHandler
是否被调用。
在您的 spring 引导主应用程序 class 中,使用 @Import
RestExceptionHandler
注册为 spring bean
@SpringBootApplication
@Import(RestExceptionHandler.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
同样在 RestExceptionHandler
中,还包含一个捕获最通用异常的方法:
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ErrorResponse handleGenericException(final Exception ex ,final WebRequest request) {
System.out.println("handleGenericException ....");
return getExceptionResponse("Failed with Invalid data" + ex.getMessage(), HttpStatus.BAD_REQUEST.value());
}
}
请告诉我 RestExceptionHandler
是否会在进行这些更改后被调用。
将@ControllerAdvice
替换为@RestControllerAdvice