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

时间:2018-02-26 14:59:49

标签: java spring spring-mvc

我想让HandlerExceptionResolver解决我不会通过@ExceptionHandler注释明确捕获的任何异常。

无论如何,我想对这些例外应用特定的逻辑。例如,另外发送邮件通知或日志。我可以通过添加@ExceptionHandler(Exception.class) catch来实现此目的:

@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?
    }
}

问题:如果我这样添加@ExceptionHandler(Exception.class),我可以捕获那些未处理的异常。

但是我不能让spring继续使用HandlerExceptionResolver创建响应ModelAndView的正常工作流程并自动设置HTTP状态代码。

例如,如果某人在POST方法上尝试使用GET,则默认情况下弹出将返回405 Method not allowed。但是@ExceptionHandler(Exception.class)我会吞下这个标准的弹簧处理......

那么如何保留默认HandlerExceptionResolver,但仍然应用我的自定义逻辑?

4 个答案:

答案 0 :(得分:4)

提供完整的解决方案:它只是通过扩展ResponseEntityExceptionHandler来工作,因为它处理所有spring-mvc错误。 然后可以使用@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 
    }
}

答案 1 :(得分:1)

好吧,我在一段时间内遇到了同样的问题,尝试了几种方法,例如扩展ResponseEntityExceptionHandler,但所有这些问题都解决了一些问题,但创造了其他问题。

然后我决定使用自定义解决方案,这也允许我发送更多信息,我已经写了下面的代码

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

}

答案 2 :(得分:0)

以这种方式为异常处理创建类

@RestControllerAdvice
public class MyExceptionHandler extends BaseExceptionHandler {

}

public class BaseExceptionHandler extends ResponseEntityExceptionHandler {

}

这里 ResponseEntityExceptionHandler 由spring提供,并覆盖它提供的与requestMethodNotSupported,missingPathVariable,noHandlerFound,typeMismatch,asyncRequestTimeouts .......相关的几个异常处理程序方法,并带有您自己的异常消息或错误响应对象和状态代码

并在MyExceptionHandler中有一个带有 @ExceptionHandler(Exception.class) 的方法,如果它没有匹配的处理程序,最后会抛出异常。

答案 3 :(得分:0)

我遇到了同样的问题并解决了它,创建了接口 HandlerExceptionResolver 的实现并从通用处理程序方法中删除了通用 @ExceptionHandler(Exception.class)。 .

它是这样工作的:

Spring 将尝试首先处理调用 MyExceptionHandler 的异常,但由于注解已从通用处理程序中删除,因此无法找到处理程序。接下来它将尝试接口 HandlerExceptionResolver 的其他实现。它将进入这个仅委托给原始通用错误处理程序的通用实现。

之后,我需要使用 ResponseEntityModelAndView 响应转换为 MappingJackson2JsonView,因为此接口需要 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
    }
}
相关问题