HandlerExceptionResolver统一异常处理 返回JSON 和 ModelAndView
统一异常处理类的两种方式一种是前后分离,一种是一整套集合返回指定到指定的错误页面显示错误信息
1.由于前后分离,是统一返回JSON的格式
自定义Exception
public class BussinessException extends RuntimeException { private static final long serialVersionUID = 1L; private int code; private String msg; public BussinessException(String msg) { super(); this.msg = msg; } public BussinessException(int code, String msg) { super(); this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
自定义统一异常处理类
import java.io.IOException; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON; import com.bshf.recipe.exception.BussinessException; import com.bshf.recipe.vo.basic.ResultVO; /** * 异常处理类 * */ @Order(-1000) public class ExceptionResolver implements HandlerExceptionResolver { private static Logger logger = LoggerFactory.getLogger("exceptionLog"); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ResultVO result = new ResultVO(); StringBuilder sb = new StringBuilder(); //处理异常 if(ex instanceof BussinessException) { resolverBussinessException(ex, sb, result); } else if (ex instanceof BindException) { resolverBindException(ex, sb, result); } else { resolverOtherException(ex, sb, result); } result.setCode(0); result.setResult(sb); result.setTime(new Date()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control", "no-cache, must-revalidate"); try { response.getWriter().write(JSON.toJSONString(result)); } catch (IOException e) { logger.error("与客户端通讯异常:" + e.getMessage(), e); e.printStackTrace(); } logger.debug("异常:" + ex.getMessage(), ex); ex.printStackTrace(); return new ModelAndView(); } /* * 处理业务层异常 */ private void resolverBussinessException(Exception ex, StringBuilder sb, ResultVO result) { BussinessException businessException = (BussinessException) ex; sb.append(businessException.getMsg()); addResult(result, "业务异常"); } /* * 处理参数绑定异常 */ private void resolverBindException(Exception ex, StringBuilder sb, ResultVO result) { BindException be = (BindException) ex; List<FieldError> errorList = be.getBindingResult().getFieldErrors(); for (FieldError error : errorList) { sb.append(error.getObjectName()); sb.append("对象的"); sb.append(error.getField()); sb.append("字段"); sb.append(error.getDefaultMessage()); } addResult(result, "参数传递异常"); } /* * 处理其他异常 */ private void resolverOtherException(Exception ex, StringBuilder sb, ResultVO result) { sb.append(ex.getMessage()); addResult(result, "其他异常"); } /* * 封装code和msg */ private void addResult(ResultVO result, String msg) { result.setMsg(msg); } }
配置文件
<!-- 定义异常处理器 --> <bean id="exceptionResolver" class="com.execption.ExceptionResolver"/>
运行流程分析
当你请求项目中某个接口时,如果报异常了,则会首先进入到这个自定义异常处理类中,然后通过判断异常类型来具体给客户端返回不同的信息提示。若接口没报异常,则此类的方法是不会运行的。
问:为什么要加@Order(-1000)
?
答:因为Spring默认有三个异常拦截器,里面的order属性分别为0,1,2,会首先去这三个拦截器中找匹配的异常,若有匹配的,则不会执行我们自定义的异常处理器。@Order(-1000)
的作用就是将顺序提到第一位,先加载我们的,有符合异常条件的,则不会继续走其他三个默认的。(我们这里一定会走,因为首先是order变成了-1000,其次是我们对自定义异常、BindException和其他做了捕获,所以一定不会执行Spring默认的)
问:为什么在最后添加了ex.printStackTrace();
答:一切为了调试方便,这样可以将异常信息打印到控制台,方便查看。
问:为什么要判断BindException?
答:一切为了调试方便。他会配合javax.validation.*中的注解一起用,比如客户端传入的参数加上了如下
@NotNull(message = "id不能为空") private Integer id; @NotEmpty(message = "title不能为空或空字符串") private String title;
这时候我调用接口如果id和title都不传的话会进入我们的自定义异常处理类中去捕获,异常类型为BindException,会返回给客户端如下json:
{ "code": 0, "msg": "参数传递异常", "result": "demoIO对象的title字段title不能为空或空字符串demoIO对象的id字段id不能为空", "time": 1491551183087 }
这样就完成了统一异常处理。若没报异常则不会执行此方法,报异常则将异常信息返回给客户端方便调试。
2.后台管理同意处理返回到指定的错误页面方式
/** * * 类名称:MyExceptionResolver.java * 类描述: * @version 1.0 */ public class MyExceptionResolver implements HandlerExceptionResolver{ protected Logger logger = Logger.getLogger(this.getClass()); public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // TODO Auto-generated method stub System.out.println("==============异常开始============="); System.out.println("request.getRemoteAddr():"+request.getRemoteAddr()); System.out.println("request.getMethod():"+request.getMethod()); System.out.println("request.getQueryString():"+request.getQueryString()); //logger.error("ex.getCause().getMessage():"+ex.getCause().getMessage()); logger.error("request.getRequestURL():"+request.getRequestURL()); //ex.printStackTrace(); System.out.println("==============异常结束============="); ModelAndView mv = new ModelAndView("error"); mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>")); return mv; } }