在详细的SSM项目开拓中,由于Controller层为处于请求处理惩罚的最顶层,再往上就是框架代码的。因此,必定需要在Controller捕捉所有异常,而且做适当处理惩罚,返回给前端一个友好的错误码。
不外,Controller一多,昆山软件开发,我们发明每个Controller里都有大量反复的、冗余的异常处理惩罚代码,极端烦琐。可否将这些反复的部门抽取出来,这样担保Controller层更专注于业务逻辑的处理惩罚,同时可以或许使得异常的处理惩罚有一个统一的节制中心点。
1. 全局异常处理惩罚
public interface HandlerExceptionResolver {
/**
* Try to resolve the given exception that got thrown during on handler execution,
* returning a ModelAndView that represents a specific error page if appropriate.
* <p>The returned ModelAndView may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to,
* or {@code null} for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
利用全局异常处理惩罚器只需要两步:
在 resolveException 中实现异常处理惩罚逻辑。从参数上,可以看到,不只可以或许拿到产生异常的函数和异常工具,还可以或许拿到 HttpServletResponse工具,从而节制本次请求返回给前端的行为。
另外,函数还可以返回一个 ModelAndView 工具,暗示渲染一个视图,例如说错误页面。不外,在前后端疏散为主流架构的本日,这个很罕用了。假如函数返回的视图为空,则暗示不需要视图。
来看一个例子:
@Component
@Slf4j
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Method method = null;
if (handler != null && handler instanceof HandlerMethod) {
method = ((HandlerMethod) handler).getMethod();
}
log.error("[{}] system error", method, ex);
ResponseDTO response = ResponseDTO.builder()
.errorCode(ErrorCode.SYSTEM_ERROR)
.build();
byte[] bytes = JSON.toJSONString(response).getBytes(StandardCharsets.UTF_8));
try {
FileCopyUtils.copy(bytes, response.getOutputStream());
} catch (IOException e) {
log.error("error", e);
throw new RuntimeException(e);
}
return new ModelAndView();
}
}
逻辑很显然,在产生异常时,将 ResponseDTO 序列化为 json 给前端。
这种异常处理惩罚只局部于某个 Controller 内,如:
@Controller
@Slf4j
@RequestMapping("/api/demo")
public class DemoController {
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseDTO<?> exceptionHandler(Exception e) {
log.error("[{}] system error", e);
return ResponseDTO.builder()
.errorCode(ErrorCode.SYSTEM_ERROR)
.build();
}
}
@ExceptionHandler 注解的要领,就会作为该Controller内部的异常处理惩罚要领。@ResponseBody 修饰,由框架的其它机制帮你序列化。另外,它还可以或许对异常范例举办细粒度的节制,通过注解可以有选择的指定异常处理惩罚要领应用的异常范例:
@ExceptionHandler({BusinessException.class, DataBaseError.class })
固然说全局异常处理惩罚HandlerExceptionResolver通过条件判定也能做到,可是利用这种注解方法明明更具有可读性。
适才说到异常处理惩罚函数可以用 @ResponseBody 修饰,就像一般的Controller要领一样。