在详细的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要领一样。