我想让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
,但仍然应用我的自定义逻辑?
答案 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
的其他实现。它将进入这个仅委托给原始通用错误处理程序的通用实现。
之后,我需要使用 ResponseEntity
将 ModelAndView
响应转换为 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
}
}