[英]How to catch all unhandled exceptions (i.e. without existing @ExceptionHandler) in Spring MVC?
I want to let HandlerExceptionResolver resolve any Exceptions that I don't explicit catch via @ExceptionHandler
annotation.我想让 HandlerExceptionResolver 解决我没有通过
@ExceptionHandler
注释显式捕获的任何异常。
Anyways, I want to apply specific logic on those exceptions.无论如何,我想对这些异常应用特定的逻辑。 Eg send a mail notification or log additionally.
例如,额外发送邮件通知或日志。 I can achieve this by adding a
@ExceptionHandler(Exception.class)
catch as follows:我可以通过添加
@ExceptionHandler(Exception.class)
捕获来实现这一点,如下所示:
@RestControllerAdvice
public MyExceptionHandler {
@ExceptionHandler(IOException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Object io(HttpServletRequest req, Exception e) {
return ...
}
@ExceptionHandler(Exception.class)
public Object exception(HttpServletRequest req, Exception e) {
MailService.send();
Logger.logInSpecificWay();
//TODO how to continue in the "normal" spring way with HandlerExceptionResolver?
}
}
Problem: if I add @ExceptionHandler(Exception.class)
like that, I can catch those unhandled exceptions.问题:如果我像这样添加
@ExceptionHandler(Exception.class)
,我可以捕获那些未处理的异常。
BUT I cannot let spring
continue the normal workflow with HandlerExceptionResolver
to create the response ModelAndView
and set a HTTP STATUS code automatically.但是我不能让
spring
继续使用HandlerExceptionResolver
的正常工作流程来创建响应ModelAndView
并自动设置 HTTP 状态代码。
Eg if someone tries a POST
on a GET
method, spring by default would return a 405 Method not allowed
.例如,如果有人在
GET
方法上尝试POST
,默认情况下 spring 将返回405 Method not allowed
。 But with an @ExceptionHandler(Exception.class)
I would swallow this standard handling of spring...但是使用
@ExceptionHandler(Exception.class)
我会吞下这种标准的 spring 处理......
So how can I keep the default HandlerExceptionResolver
, but still apply my custom logic?那么如何保留默认的
HandlerExceptionResolver
,但仍然应用我的自定义逻辑?
To provide a complete solution: it works just by extending ResponseEntityExceptionHandler
, as that handles all the spring-mvc
errors.提供一个完整的解决方案:它只是通过扩展
ResponseEntityExceptionHandler
,因为它可以处理所有spring-mvc
错误。 And the ones not handled can then be caught using @ExceptionHandler(Exception.class)
.然后可以使用
@ExceptionHandler(Exception.class)
捕获@ExceptionHandler(Exception.class)
的那些。
@RestControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> exception(Exception ex) {
MailService.send();
Logger.logInSpecificWay();
return ... custom exception
}
}
Well, I was facing the same problem some time back and have tried several ways like extending ResponseEntityExceptionHandler
but all them were solving some problems but creating other ones.好吧,我前段时间遇到了同样的问题,并尝试了几种方法,例如扩展
ResponseEntityExceptionHandler
但所有这些方法都在解决一些问题,但创建了其他问题。
Then I have decided to go with a custom solution which was also allowing me to send additional information and I have written below code然后我决定使用自定义解决方案,该解决方案也允许我发送其他信息,并且我编写了以下代码
@RestControllerAdvice
public class MyExceptionHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(NumberFormatException.class)
public ResponseEntity<Object> handleNumberFormatException(NumberFormatException ex) {
return new ResponseEntity<>(getBody(BAD_REQUEST, ex, "Please enter a valid value"), new HttpHeaders(), BAD_REQUEST);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException ex) {
return new ResponseEntity<>(getBody(BAD_REQUEST, ex, ex.getMessage()), new HttpHeaders(), BAD_REQUEST);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException ex) {
return new ResponseEntity<>(getBody(FORBIDDEN, ex, ex.getMessage()), new HttpHeaders(), FORBIDDEN);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> exception(Exception ex) {
return new ResponseEntity<>(getBody(INTERNAL_SERVER_ERROR, ex, "Something Went Wrong"), new HttpHeaders(), INTERNAL_SERVER_ERROR);
}
public Map<String, Object> getBody(HttpStatus status, Exception ex, String message) {
log.error(message, ex);
Map<String, Object> body = new LinkedHashMap<>();
body.put("message", message);
body.put("timestamp", new Date());
body.put("status", status.value());
body.put("error", status.getReasonPhrase());
body.put("exception", ex.toString());
Throwable cause = ex.getCause();
if (cause != null) {
body.put("exceptionCause", ex.getCause().toString());
}
return body;
}
}
Create classes for exception handling in this way以这种方式创建异常处理类
@RestControllerAdvice
public class MyExceptionHandler extends BaseExceptionHandler {
}
public class BaseExceptionHandler extends ResponseEntityExceptionHandler {
}
Here ResponseEntityExceptionHandler is provided by spring and override the several exception handler methods provided by it related to the requestMethodNotSupported,missingPathVariable,noHandlerFound,typeMismatch,asyncRequestTimeouts ....... with your own exception messages or error response objects and status codes这里ResponseEntityExceptionHandler由 spring 提供并覆盖它提供的与 requestMethodNotSupported、missingPathVariable、noHandlerFound、typeMismatch、asyncRequestTimeouts 相关的几个异常处理程序方法......使用您自己的异常消息或错误响应对象和状态代码
and have a method with @ExceptionHandler(Exception.class)
in MyExceptionHandler where the thrown exception comes finally if it doesn't have a matching handler.并在 MyExceptionHandler 中有一个带有
@ExceptionHandler(Exception.class)
的方法,如果它没有匹配的处理程序,则抛出的异常最终会出现。
I had the same issue and solved it creating a implementation of the interface HandlerExceptionResolver
and removing the generic @ExceptionHandler(Exception.class)
from the generic handler method.我有同样的问题并解决了它创建接口
HandlerExceptionResolver
的实现并从通用处理程序方法中删除通用@ExceptionHandler(Exception.class)
。 . .
It works this way:它是这样工作的:
Spring will try to handle the exception calling MyExceptionHandler
first, but it will fail to find a handler because the annotation was removed from the generic handler. Spring 将尝试首先处理调用
MyExceptionHandler
的异常,但由于注解已从通用处理程序中删除,因此将无法找到处理程序。 Next it will try other implementations of the interface HandlerExceptionResolver
.接下来它将尝试接口
HandlerExceptionResolver
其他实现。 It will enter this generic implementation that just delegates to the original generic error handler.它将进入这个仅委托给原始通用错误处理程序的通用实现。
After that, I need to convert the ResponseEntity
response to ModelAndView
using MappingJackson2JsonView
because this interface expects a ModelAndView
as return type.之后,我需要使用
MappingJackson2JsonView
将ResponseEntity
响应转换为ModelAndView
因为此接口需要ModelAndView
作为返回类型。
@Component
class GenericErrorHandler(
private val errorHandler: MyExceptionHandler,
private val objectMapper: ObjectMapper
) : HandlerExceptionResolver {
override fun resolveException(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception): ModelAndView? {
// handle exception
val responseEntity = errorHandler.handleUnexpectedException(ex)
// prepare JSON view
val jsonView = MappingJackson2JsonView(objectMapper)
jsonView.setExtractValueFromSingleKeyModel(true) // prevents creating the body key in the response json
// prepare ModelAndView
val mv = ModelAndView(jsonView, mapOf("body" to responseEntity.body))
mv.status = responseEntity.statusCode
mv.view = jsonView
return mv
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.