简体   繁体   English

如何在 Spring MVC 中捕获所有未处理的异常(即没有现有的 @ExceptionHandler)?

[英]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.之后,我需要使用MappingJackson2JsonViewResponseEntity响应转换为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.

相关问题 Spring MVC @ExceptionHandler无法捕获异常 - Spring MVC @ExceptionHandler failing to catch an exception Spring Portlet MVC-如何捕获映射异常 - Spring Portlet MVC - how to catch mapping exceptions @ExceptionHandler不会捕获从Spring Formatters抛出的异常 - @ExceptionHandler doesn't catch exceptions being thrown from Spring Formatters 如何在 Spring @ExceptionHandler 中捕获自定义注释? - How to catch custom annotations in Spring @ExceptionHandler? Spring-mvc中所有控制器的@ExceptionHandler - @ExceptionHandler for all controllers in spring-mvc 在Spring Boot应用程序中,我将如何捕获内部抛出的异常,这些异常本来会逃逸到客户端,但不会捕获Spring MVC异常? - In a Spring Boot application, how would I catch internally thrown exceptions that would otherwise escape to the client, but not Spring MVC exceptions? 在Spring Mvc Java中记录未处理的异常 - Logging unhandled exceptions in Spring Mvc Java 如何在 Room 持久化库中捕获未处理的异常 - How to catch unhandled exceptions in the Room persistence library Spring 3.0 MVC ExceptionHandler - Spring 3.0 MVC ExceptionHandler 如何在 spring 引导之上编写框架(即在不实现某些接口的情况下编写 spring 引导应用程序) - How to write a framework on top of spring boot (i.e. write a spring boot application without implementing some interfaces)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM