Spring MVC 全局异常处理-RESTAPI接口返回统一JSON格式-自定义异常处理--404异常捕捉
2017-06-08 17:41
1141 查看
写之前大概两周草草的将一些代码保存在草稿箱,今天有空来看,结果都没有了【怨念】—重新整理一下了 —–【转载请标注出处】
第一部分:需求
第二部分:实现方式
第三部分:404异常捕捉不能实现分析
第四部分:原因和源码分析
第五部分:最终总结
1. SimpleMappingExceptionResolver
采用xml配置方式,代码零入侵 。自由性不足,获取异常信息少(只有异常信息)。
2. HandlerExceptionResolver
自定义类实现该类。可以实现统一异常处理里。我在实现的时候却没有生效,而是走了其自定义的DefaultHandlerExceptionResolver,具体原因下面分析
3. @ExceptionHandler注解
这种捕捉方式要求和被捕捉Controller在同一个类中,一般实现方式是把ExceptionHandler放在BaseController中继承,自由性差。
4.@ControllerAdvice注解
这种需要配合@ExceptionHandler属于第三种方式变种,我使用的则是这种方式的变种,下面详解。
上面是简化的请求流转图,感谢某位同学的图片。
下面是程序员赵鑫的原理分析图,我搬来一用。当然我没有授权,如果有争议,我援引互联网信息共享条例自护(黑人问号脸)
照例感谢程序员赵鑫同学,想看再详细的分析请转贴这里
上图是SpringMVC的异常处理器结构,HandlerExceptionResolver是一个接口,留给自定义的时候使用。
异常处理器的处理顺序是:异常->HandlerExceptionResolver->自定义异常->默认异常处理器
SpringMVC的异常处理器通过实现Orderd接口,定义Order数值为每个异常处理器排序,图中可以看到默认异常处理器都继承于AbstractHandlerExceptionResolver。看图:
默认异常处理器都实现了doResolverException()方法。
图上的每一个处理器其实都代表了可以实现全局自定义处理的一种或者多种方式。
图中的属性 throwExceptionIfNoHandlerFound = false 就是404异常抛出错误与否的判断属性,下面是判断源码,throwExceptionIfNoHandlerFound为true则会抛出异常
1.SimpleMappingExceptionResolver
代码如下:这里不做详解,缺点是不灵活,获取信息参数有限
2.HandlerExceptionResolver
这种实现方式会受到容器加载顺序影响(可能是这个原因),如果没有加载完全,会按照SpringMVC默认的配置文件中的处理顺序进行处理
<
abcc
blockquote>
这种需要在Spring的配置文件applicationContext.xml中增加以下内容:
3.ControllerAdvice(和ExceptionHandler放在一起了)
4.ControllerAdvice(我的实现方式)
第一部分:需求
第二部分:实现方式
第三部分:404异常捕捉不能实现分析
第四部分:原因和源码分析
第五部分:最终总结
需求
本意是想针对对外REST接口的返回格式进行统一,结果404错误始终无法被捕捉
实现方式
对于目前的主流方式全部尝试了一遍,主要有三种,还有其他一些异类采用Filter之类的方式实现,如需可以自取1. SimpleMappingExceptionResolver
采用xml配置方式,代码零入侵 。自由性不足,获取异常信息少(只有异常信息)。
2. HandlerExceptionResolver
自定义类实现该类。可以实现统一异常处理里。我在实现的时候却没有生效,而是走了其自定义的DefaultHandlerExceptionResolver,具体原因下面分析
3. @ExceptionHandler注解
这种捕捉方式要求和被捕捉Controller在同一个类中,一般实现方式是把ExceptionHandler放在BaseController中继承,自由性差。
4.@ControllerAdvice注解
这种需要配合@ExceptionHandler属于第三种方式变种,我使用的则是这种方式的变种,下面详解。
原因和源码分析
首先来分析异常的处理过程上面是简化的请求流转图,感谢某位同学的图片。
下面是程序员赵鑫的原理分析图,我搬来一用。当然我没有授权,如果有争议,我援引互联网信息共享条例自护(黑人问号脸)
照例感谢程序员赵鑫同学,想看再详细的分析请转贴这里
上图是SpringMVC的异常处理器结构,HandlerExceptionResolver是一个接口,留给自定义的时候使用。
异常处理器的处理顺序是:异常->HandlerExceptionResolver->自定义异常->默认异常处理器
SpringMVC的异常处理器通过实现Orderd接口,定义Order数值为每个异常处理器排序,图中可以看到默认异常处理器都继承于AbstractHandlerExceptionResolver。看图:
默认异常处理器都实现了doResolverException()方法。
图上的每一个处理器其实都代表了可以实现全局自定义处理的一种或者多种方式。
404异常捕捉不能实现分析
在我实现全局处理异常的时候,发现404异常并没有被捕捉,参考多方资料后发现,SpringleMVC的机制默认是对404异常不进行抛出动作的,直接在Response中设置错误代码,直接返回。具体看图:图中的属性 throwExceptionIfNoHandlerFound = false 就是404异常抛出错误与否的判断属性,下面是判断源码,throwExceptionIfNoHandlerFound为true则会抛出异常
原因和源码分析
下面对几种实现方式进行解析1.SimpleMappingExceptionResolver
代码如下:这里不做详解,缺点是不灵活,获取信息参数有限
<!-- 出现异常会跳到这个页面,总错误处理--> <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView"> <value>/error/error</value> </property> <property name="defaultStatusCode"> <value>500</value> </property> <property name="warnLogCategory"> <value>org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver</value> </property> <!-- 如果不设置exceptionMappings,就会全局异常捕获 --> <property name="exceptionMappings"> <props> <prop key="Java.sql.SQLException">/error/error</prop> <prop key="Java.lang.RuntimeException">/error/error</prop> <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">/error/error</prop> </props> </property> </bean>
2.HandlerExceptionResolver
这种实现方式会受到容器加载顺序影响(可能是这个原因),如果没有加载完全,会按照SpringMVC默认的配置文件中的处理顺序进行处理
<
abcc
blockquote>
@Component public class MyExceptionHandler implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Map<String, Object> model = new HashMap<String, Object>(); model.put("ex", ex); // 根据不同错误转向不同页面 if(ex instanceof BusinessException) { return new ModelAndView("error-business", model); }else if(ex instanceof ParameterException) { return new ModelAndView("error-parameter", model); } else { return new ModelAndView("error", model); } } }
这种需要在Spring的配置文件applicationContext.xml中增加以下内容:
//有一种说法,如果不起作用,id写成这样说不定能解决问题,因为加载的时候是按照这个id去加载的 < bean id="handlerExceptionResolver" class="cn.basttg.core.exception.MyExceptionHandler"/>
3.ControllerAdvice(和ExceptionHandler放在一起了)
@ControllerAdvice public class ExceptionHandler { @ResponseBody @org.springframework.web.bind.annotation.ExceptionHandler(Exception.class//这里可以选择其他具体的异常) public ResponseModel handleUnexpectedServerError(Exception ex) { ex.printStackTrace(); // 处理异常 ResponseModel response = new ResponseModel(); String errorMsg = ex.getMessage(); response.setCode(Integer.valueOf(ResultCode.SYSTEM_EXCEPTION)); if(errorMsg.contains("excpStart")){ errorMsg = errorMsg.substring(errorMsg.indexOf("excpStart") + 9, errorMsg.indexOf("excpEnd")); } response.setMessage(errorMsg); // 返回数据 return response; }
4.ControllerAdvice(我的实现方式)
@ControllerAdvice public class GlobalExceptionResolver extends DefaultHandlerExceptionResolver { @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String url = request.getServletPath(); if (url.startsWith("/api")) {//api返回异常拦截 if (ex instanceof HttpRequestMethodNotSupportedException) { setResponseParam(response, 405, "请求方式错误!"); return null; } if (ex instanceof MissingServletRequestParameterException) { setResponseParam(response, 400, "错误请求!"); return null; } if (ex instanceof NoHandlerFoundException) { //可以进行其他方法处理,LOG或者什么详细记录,我这里直接返回JSON setResponseParam(response, 404, "请求路径错误!"); return null; } setResponseParam(response, 500, "服务器内部错误!服务暂时不可用!"); return null; } //这里调用父类的异常处理方法,实现其他不需要的异常交给SpringMVC处理 return super.doResolveException(request, response, handler, ex); } private void setResponseParam(HttpServletResponse response, int code, String msg) throws IOException { JSONObject j = JSONObject.fromObject(R.error(code, msg)); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().print(j.toString()); } }
参考:
公子的专栏、焰尾迭、程序猿之洞、Exception Handling in Spring MVC、程序员赵鑫、最终总结
写了近3小时,一边回忆一边写,好累,下次有好的再说吧,需要支持点这里相关文章推荐
- RxJava + Retrofit+okhttp 封装,包含对相同格式请求数据、相同格式返回数据处理,显示 Material Design 加载 dialog,文件上传下载进度展示、全局异常捕捉。
- 解决spring boot中rest接口404,500等错误返回统一的json格式
- 解决spring boot中rest接口404,500等错误返回统一的json格式
- Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
- spring boot 全局统一格式返回自定义异常信息
- Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
- Spring MVC 异常处理统一为json格式
- spring mvc统一处理异常时候返回json或xml
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- SpringBoot07 异常枚举、自定义异常、统一的全局异常处理
- Spring MVC全局异常后返回JSON异常数据
- Spring boot 自定义统一异常处理(以及规范响应格式)
- SpringMVC 异常统一处理,返回json
- Spring boot异常统一处理方法:@ControllerAdvice注解的使用、全局异常捕获、自定义异常捕获
- spring mvc 自定义注解ResponseEncryptBody、RequestDecryptBody统一处理加密、解密数据,供移动端使用的rest服务
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- SpringBoot学习——全局异常处理设置(返回JSON)
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- Strust2 拦截器处理返回自定义json格式数据
- Spring MVC全局异常后返回JSON异常数据