如何使用@ExceptionHandler 捕获 HTTP 405“不允许的方法”异常?
Posted
技术标签:
【中文标题】如何使用@ExceptionHandler 捕获 HTTP 405“不允许的方法”异常?【英文标题】:How to catch HTTP 405 "Method Not Allowed" exception using @ExceptionHandler? 【发布时间】:2021-02-13 22:33:56 【问题描述】:我创建了一个 REST 应用程序并添加了一个类来处理异常:
@RestControllerAdvice(basePackages = "com.foxminded.university.api.controller")
public class ApiGlobalExceptionHandler extends ResponseEntityExceptionHandler
private static final String API_UNHANDLED_EXCEPTION = "REST API reached unhandled exception: %s";
private static final HttpStatus internalServerError = HttpStatus.INTERNAL_SERVER_ERROR;
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handle(Exception e)
ExceptionDetail exceptionDetail = new ExceptionDetail(
String.format(API_UNHANDLED_EXCEPTION, e.getMessage()),
internalServerError,
ZonedDateTime.now(ZoneId.systemDefault()));
return new ResponseEntity<Object>(exceptionDetail, internalServerError);
@Override
public ResponseEntity<Object> handleTypeMismatch(
TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
ExceptionDetail exceptionDetail = new ExceptionDetail(
String.format(API_UNHANDLED_EXCEPTION, ex.getMessage()),
internalServerError,
ZonedDateTime.now(ZoneId.systemDefault()));
return new ResponseEntity<Object>(exceptionDetail, internalServerError);
@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers,
HttpStatus status, WebRequest request)
ExceptionDetail exceptionDetail = new ExceptionDetail(
String.format(API_UNHANDLED_EXCEPTION, e.getMessage()),
internalServerError,
ZonedDateTime.now(ZoneId.systemDefault()),
e.getBindingResult().getFieldErrors());
return new ResponseEntity<Object>(exceptionDetail, internalServerError);
当我通过邮递员 http://localhost:8080/api/groups/jkjk 而不是 http://localhost:8080/api/groups 发送错误的发布请求时
它抛出了一个我在调试时无法捕获初始化的异常,无论是在 ApiGlobalExceptionHandler 类中还是在 ResponseEntityExceptionHandler 类中:
"timestamp": 1604171144423,
"status": 405,
"error": "Method Not Allowed",
"message": "",
"path": "/api/groups/jkjk"
我可以捕获的所有其他异常。如何捕获此异常以添加自定义处理?
【问题讨论】:
【参考方案1】:您只需在其签名中添加一个带有MethodNotAllowedException
的新方法。
@ExceptionHandler(value = MethodNotAllowedException.class)
public ResponseEntity<Object> handleMethodNotAllowedExceptionException(MethodNotAllowedException ex)
return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, null, null, ex.getMessage(), null);
private ResponseEntity<Object> buildResponseEntity(HttpStatus status, HttpHeaders headers, Integer internalCode, String message, List<Object> errors)
ResponseBase response = new ResponseBase() //A generic ResponseBase class
.success(false)
.message(message)
.resultCode(internalCode != null ? internalCode : status.value())
.errors(errors != null
? errors.stream().filter(Objects::nonNull).map(Objects::toString).collect(Collectors.toList())
: null);
return new ResponseEntity<>((Object) response, headers, status);
您可以随意自定义buildResponseEntity
。
更新
我重新审视了我的答案,因为它不符合您的要求。所以,它是这样的:
我发送一个接受GET
的方法的post 请求。这将触发 Request method 'POST' not supported,如下所示。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.查找路径 /v1/user/profile/1 的处理程序方法 org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.Resolving 来自处理程序 [null] 的异常:org.springframework.web.HttpRequestMethodNotSupportedException:不支持请求方法“POST” org.springframework.beans.factory.support.DefaultListableBeanFactory.Returning 单例 bean 'ethGlobalExceptionHandler' 的缓存实例
在这种情况下,无需添加
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
.
其实如果这样做的话,会抛出下面的错误(因为已经处理过了),
java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.HttpRequestMethodNotSupportedException]:....
所以,解决办法是:
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request)
return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, headers, null, ex.getMessage(), Arrays.asList(""));
【讨论】:
但是为什么它没有被 @ExceptionHandler(Exception.class) 的句柄方法捕获?我预计它应该捕获 ResponseEntityExceptionHandler 方法未捕获的所有内容... 你是从ResponseEntityExceptionHandler
扩展而来的,它只提供标准 Spring MVC 异常的处理。
我用下面的注解@ExceptionHandler (MethodNotAllowedException.class)
向rest控制器添加了一个异常处理程序,但问题仍然存在。调试时光标不跳转到该方法,响应为默认。
我不确定我是否完全理解了你的所有观点,但这是我的疑问: 1. 你写了“我发送一个接受 GET 的方法的 post 请求。这将触发请求方法 'POST ' 不支持,如下所示。”它接受获取和发布。获取接收实体列表并发布以创建新实体。 2.你写了“事实上如果你这样做,会抛出以下错误(因为它已经被处理了)”。对于从 ResponseEntityExceptionHandler 扩展,我说的是 MethodNotAllowedException.class,而不是 HttpRequestMethodNotSupportedException.class。没有错误...
查看这个简短的描述evertpot.com/http/405-method-not-allowed【参考方案2】:
我在这里找到了解释Custom handling for 405 error with Spring Web MVC
上面写着The reason your @ExceptionHandler annotated method never catches your exception is because these ExceptionHandler annotated methods are invoked after a successful Spring controller handler mapping is found. However, your exception is raised before that.
并且解决方案不是从 ResponseEntityExceptionHandler 类扩展,而是从 DefaultHandlerExceptionResolver 扩展并覆盖它的 handleHttpRequestMethodNotSupported 方法。
【讨论】:
这将返回视图。在您的情况下,您期望的应该是@ResponseBody
。
如果检查 DefaultHandlerExceptionResolver 的代码,handleHttpRequestMethodNotSupported 方法只返回空视图。那里的主要工作是"response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());"
。因此,实际上它为响应配置错误,但不准备视图。你能具体说明一下,你为什么不喜欢这个答案,你怀疑什么?
但是,您不必将错误内容发送到响应正文吗?
正如我从我提到的帖子中了解到的(顺便说一句,它已经解决了问题),对于参数类型错误的帖子操作,不可能使用@ErrorHandling 注释重写默认错误处理全部...如果我错了,请纠正我。所以,也许还有另一种方法,但我没有找到任何其他解释,正如我所说,我无法通过你的方法捕捉到这个异常......也许我做错了......但是,当您正在为重写的 handleHttpRequestMethodNotSupported 方法调试代码,您是否能够在断点处停止返回语句?
只是为了说明问题,有一个映射到某个实体列表的休息控制器,并且有一个映射的 post 方法,当您发送诸如“site.com\entities”之类的 URI 的 post 请求时,它会创建新实体”。但是,当您为“site.com\entities\abc”发送帖子时,它会给出 405 状态的错误响应,我无法捕捉到。以上是关于如何使用@ExceptionHandler 捕获 HTTP 405“不允许的方法”异常?的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot(十九)@ControllerAdvice+@ExceptionHandler全局捕获Controller异常
当@Valid body 不满足并且@ExceptionHandler 命中时获取标题
springboot + @ControllerAdvice + @ExceptionHandler 实现全局异常拦截,不用