gpt4 book ai didi

java - 重写标准 Spring MVC 异常的处理行为

转载 作者:行者123 更新时间:2023-12-01 21:21:29 25 4
gpt4 key购买 nike

Spring Boot 似乎有处理某些异常的默认行为。我有一个休息 Controller 。如果我不在 @ControllerAdvice 注释的休息 Controller 中处理 HttpRequestMethodNotSupportedException,应用程序将返回包含错误消息的默认 JSON 响应。

我不想替换此 JSON 响应,但我确实想在发生这种情况时记录其他信息(例如记录某些请求者的 IP 地址)。

有没有办法使用 @ExceptionHandler 带注释的方法或其他机制来做到这一点?

最佳答案

Spring MVC 确实为您配置了一个异常处理程序。
默认情况下,DefaultHandlerExceptionResolver按照 javadoc 类中的说明使用:

Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes.

This exception resolver is enabled by default in the common Spring org.springframework.web.servlet.DispatcherServlet.
That is right for MVC controllers.

但是对于 REST Controller 的异常处理程序(此处是您的要求),Spring 依赖于 ResponseEntityExceptionHandler 类。
第一个类具有返回 ModelAndView 的方法,而第二个类具有返回 ReponseEntity 的方法。

在两种情况下(MVC 和 REST Controller ),您都可以通过使用 @ControllerAdvice 注释您的类来定义自定义异常处理程序,但由于您的要求是 REST Controller ,因此让我们重点关注这一点。

除了使用 @ControllerAdvice 注释自定义异常处理程序之外,您还可以扩展基本异常处理程序类,例如 ResponseEntityExceptionHandler覆盖某些行为。
ResponseEntityExceptionHandler 实现允许了解实际处理和映射的所有异常。查看 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);
}
}

所以问题是:如何重写特定异常的异常处理程序?
这种方法行不通:

@ExceptionHandler(value = { HttpRequestMethodNotSupportedException.class })
protected ResponseEntity<Object> handleConflict(HttpRequestMethodNotSupportedException ex, WebRequest request) {
...
}

因为在异常处理程序类中,Spring 不允许您多次定义特定 Exception 子类的映射。因此,不允许在自定义异常处理程序中添加此映射,因为 Spring 已在 ResponseEntityExceptionHandler 类中定义了该异常的映射。
具体来说,它会阻止 Spring 容器成功启动。
您应该得到一个异常,例如:

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.
So in your case (overriding the handler of HttpRequestMethodNotSupportedException), just override handleHttpRequestMethodNotSupported() that is what you are looking for :

if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}

例如这样:

@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);
}
}

关于java - 重写标准 Spring MVC 异常的处理行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51216326/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com