[英]Overriding the handling behavior for standard Spring MVC exceptions
Spring Boot seems to have a default behavior for handling certain exceptions. Spring Boot 似乎有处理某些异常的默认行为。 I have a rest controller.
我有一个休息控制器。 If I do not handle
HttpRequestMethodNotSupportedException
in the @ControllerAdvice
annotated rest controller, the application returns a default JSON response containing the error message.如果我不在
@ControllerAdvice
注释的休息控制器中处理HttpRequestMethodNotSupportedException
,应用程序将返回包含错误消息的默认 JSON 响应。
I do not want to replace this JSON response, but I do want to log additional information (eg log certain requestor's IP address) when it happens.我不想替换这个 JSON 响应,但我想在它发生时记录附加信息(例如记录某些请求者的 IP 地址)。
Is there a way to do this with an @ExceptionHandler
annotated method, or other mechanism?有没有办法用
@ExceptionHandler
注释方法或其他机制来做到这一点?
Spring MVC configures indeed an exception handler for you. Spring MVC 确实为您配置了一个异常处理程序。
By default, DefaultHandlerExceptionResolver
is used as stated in the class javadoc :默认情况下,
DefaultHandlerExceptionResolver
按照类 javadoc 中的说明使用:
Default implementation of the
HandlerExceptionResolver
interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes.HandlerExceptionResolver
接口的默认实现,用于解析标准 Spring 异常并将它们转换为相应的 HTTP 状态代码。This exception resolver is enabled by default in the common Spring
org.springframework.web.servlet.DispatcherServlet
.默认情况下,在公共 Spring
org.springframework.web.servlet.DispatcherServlet
启用此异常解析器。
That is right for MVC controllers.这适用于 MVC 控制器。
But for exception handlers for REST controllers (your requirement here), Spring rely on the ResponseEntityExceptionHandler
class.但是对于 REST 控制器的异常处理程序(这里是您的要求),Spring 依赖于
ResponseEntityExceptionHandler
类。
The first class have methods that return ModelAndView
s while the second class have methods that return ReponseEntity
s.第一个类具有返回
ModelAndView
的方法,而第二个类具有返回ReponseEntity
的方法。
You can define a custom exception handler by annotating your class with @ControllerAdvice
in both cases (MVC and REST controllers) but since your requirement is for REST controllers, let's focus on that.在两种情况下(MVC 和 REST 控制器),您都可以通过使用
@ControllerAdvice
注释您的类来定义自定义异常处理程序,但由于您的要求是针对 REST 控制器,让我们专注于这一点。
Besides annotating a custom exception handler with @ControllerAdvice
, you can also make that to extend a base exception handler class such as ResponseEntityExceptionHandler
to override some behaviors.除了使用
@ControllerAdvice
注释自定义异常处理程序之外,您还可以通过扩展基本异常处理程序类(例如ResponseEntityExceptionHandler
来覆盖某些行为。
ResponseEntityExceptionHandler
implementations allows to know all the exceptions actually handled and mapped. ResponseEntityExceptionHandler
实现允许知道所有实际处理和映射的异常。 Look at the handleException()
method that is the facade method of the ResponseEntityExceptionHandler
class :查看
handleException()
方法,它是ResponseEntityExceptionHandler
类的外观方法:
/**
* Provides handling for standard Spring MVC exceptions.
* @param ex the target exception
* @param request the current request
*/
@ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
}
else if (ex instanceof MissingPathVariableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleMissingPathVariable((MissingPathVariableException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestParameterException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
}
else if (ex instanceof ServletRequestBindingException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
}
else if (ex instanceof ConversionNotSupportedException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof TypeMismatchException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotReadableException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotWritableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
}
else if (ex instanceof MethodArgumentNotValidException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestPartException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
}
else if (ex instanceof BindException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleBindException((BindException) ex, headers, status, request);
}
else if (ex instanceof NoHandlerFoundException) {
HttpStatus status = HttpStatus.NOT_FOUND;
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
}
else if (ex instanceof AsyncRequestTimeoutException) {
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
return handleAsyncRequestTimeoutException(
(AsyncRequestTimeoutException) ex, headers, status, request);
}
else {
if (logger.isWarnEnabled()) {
logger.warn("Unknown exception type: " + ex.getClass().getName());
}
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(ex, null, headers, status, request);
}
}
So the question is : how to override the exception handler for a specific exception ?所以问题是:如何覆盖特定异常的异常处理程序?
This approach cannot work :这种方法行不通:
@ExceptionHandler(value = { HttpRequestMethodNotSupportedException.class })
protected ResponseEntity<Object> handleConflict(HttpRequestMethodNotSupportedException ex, WebRequest request) {
...
}
Because inside the exception handler class, Spring doesn't let you to define more than a single time a mapping for a specific Exception
subclass.因为在异常处理程序类中,Spring 不允许您多次定义特定
Exception
子类的映射。 So adding this mapping in your custom exception handler is not allowed because Spring already defines a mapping for that exception in the ResponseEntityExceptionHandler
class.因此,不允许在自定义异常处理程序中添加此映射,因为 Spring 已经在
ResponseEntityExceptionHandler
类中为该异常定义了一个映射。
Concretely, it will prevent the Spring container from starting successfully.具体来说,它会阻止 Spring 容器成功启动。
You should get an exception such as :你应该得到一个异常,例如:
Caused by: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.HttpRequestMethodNotSupportedException]: {protected org.springframework...
To ease client subclasses to override the actual handling/mapping for a specific exception, Spring implemented the logic of each exception caught and handled by itself in a protected
method of the ResponseEntityExceptionHandler
class.为了简化客户端子类以覆盖特定异常的实际处理/映射,Spring 在
ResponseEntityExceptionHandler
类的protected
方法中实现了由自身捕获和处理的每个异常的逻辑。
So in your case (overriding the handler of HttpRequestMethodNotSupportedException
), just override handleHttpRequestMethodNotSupported()
that is what you are looking for :因此,在您的情况下(覆盖
HttpRequestMethodNotSupportedException
的处理程序),只需覆盖您正在寻找的handleHttpRequestMethodNotSupported()
:
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
For example in this way :例如以这种方式:
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
// do your processing
...
// go on (or no) executing the logic defined in the base class
return super.handleHttpRequestMethodNotSupported(ex, headers, status, request);
}
}
Thanks davidxxx for this answer and examples.感谢 davidxxx 提供此答案和示例。 They helped me a lot.
他们帮了我很多。 thank you!
谢谢你! I did it this way and it worked for me.
我是这样做的,它对我有用。 Here is an example:
下面是一个例子:
@ControllerAdvice
public class AppExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(final ResourceNotFoundException ex, final WebRequest request) {
final ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(ResourceAlreadyExistsFoundException.class)
public ResponseEntity<?> resourceAlreadyExistsFoundException(final ResourceAlreadyExistsFoundException ex, final WebRequest request) {
final ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ConstraintViolationException.class)
ResponseEntity<?> onConstraintValidationException(
ConstraintViolationException e) {
List<ErrorDetails> errors = new ArrayList<>();
for (ConstraintViolation violation : e.getConstraintViolations()) {
errors.add(
new ErrorDetails(new Date(), violation.getPropertyPath().toString(), violation.getMessage()));
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ArrayList<ErrorDetails> errors = new ArrayList<>();
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
errors.add(
new ErrorDetails(new Date(), fieldError.getField(), fieldError.getDefaultMessage()));
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> globleExcpetionHandler(final Exception ex, final WebRequest request) {
final ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.