SpringBoot全局异常处理(续)
2018-01-31 16:36
951 查看
在上一篇《SpringBoot全局异常处理》中介绍了两种处理全局异常的方案,今天我们继续来探讨一下如何更好地处理异常,比如:404之类的错误。
首先来怀念一下默认的错误页面,默哀三分钟。
哈哈,SpringBoot默认的错误页面就是一个白底页面加了一些错误信息。我们今天要解决的问题就是在之前处理异常的基础上来修改默认的错误页面,即自定义诸如404、500酱紫的错误页面,关于处理这些问题的博客数不胜数,但是经我测试了一些之后,不是太满意,所以另辟蹊径,整理一下。
第一种方案:配置:
这种方式是网上提到的比较多的,可以将该参数设置为
我们需要做的就是同时配置
但是使用这种做法后,我们会发现,页面上的静态资源无法加载了,为啥呢?因为我们配置了
第二种方案:实现
通过自定义
这样,当系统出现错误的时候,就会执行我们自己的Controller,从而跳转到我们自定义的error页面。这只是最简单的实现方式,有时候我们需要针对不同的状态码,跳转到不同的页面,那么我们可以添加我们实际的逻辑如下:
使用这种方式,可操作性更大一些点,也没有特殊的配置需要注意。我们还已将这种方式与前一篇文章结合起来,出现404状态码时直接抛出
综合本篇文章与之前的《SpringBoot全局异常处理》,只是为大家提供了处理异常以及错误页面的思路,大家可以自由发挥进行组合扩展,下面附上一份针对前一篇文章中的方案二中的
我本人比较喜欢这种全局异常处理结合实现
2018年03月02日补充开始
在实际使用中发现如果使用
2018年03月02日补充结束
首先来怀念一下默认的错误页面,默哀三分钟。
哈哈,SpringBoot默认的错误页面就是一个白底页面加了一些错误信息。我们今天要解决的问题就是在之前处理异常的基础上来修改默认的错误页面,即自定义诸如404、500酱紫的错误页面,关于处理这些问题的博客数不胜数,但是经我测试了一些之后,不是太满意,所以另辟蹊径,整理一下。
第一种方案:配置:
spring.mvc.throw-exception-if-no-handler-found
这种方式是网上提到的比较多的,可以将该参数设置为
true,当请求找不到对应的
HandlerMethod时抛出
NoHandlerFoundException异常,这样通过我们上一篇博客的处理方式就可以捕获到该异常,然后进行处理,但是很多情况下,该配置并不会生效。依然会显示默认的错误页面,针对这种方案,我总结了一下,如果需要还配置生效抛出异常。
我们需要做的就是同时配置
spring.resources.add-mappings: false,这样的话当出现404错误的时候就会抛出异常,我们就可以针对异常进行处理。
但是使用这种做法后,我们会发现,页面上的静态资源无法加载了,为啥呢?因为我们配置了
add-mappings为false,导致了静态资源不处理,所以当我们还要处理静态资源的时候,我们就可以默认
add-mappings配置为
true,不进行修改,通过同时配置
spring.tatic-path-pattern和
spring.resources.static-locations实现指定的资源路径交由SpringBoot处理,这样可以解决大部分问题,但是有个小毛病就是,当我们的访问我们配置的资源路径出现404的时候,依然会显示默认页面,所以总体来说,这种方案不太理想。
第二种方案:实现
ErrorController
通过自定义
ErrorController,我们可以重写默认的错误逻辑,举个栗子:
package com.jianggujin.hr.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class BaseErrorController implements ErrorController { @Override public String getErrorPath() { return "error"; } @RequestMapping(value = "/error") public String error(HttpServletRequest request, HttpServletResponse response) { return getErrorPath(); } }
这样,当系统出现错误的时候,就会执行我们自己的Controller,从而跳转到我们自定义的error页面。这只是最简单的实现方式,有时候我们需要针对不同的状态码,跳转到不同的页面,那么我们可以添加我们实际的逻辑如下:
package com.jianggujin.hr.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class BaseErrorController implements ErrorController { @Override public String getErrorPath() { return "error"; } @RequestMapping(value = "/error") public String error(HttpServletRequest request, HttpServletResponse response) { if (response.getStatus() == HttpStatus.NOT_FOUND.value()) { return "404"; } return getErrorPath(); } }
使用这种方式,可操作性更大一些点,也没有特殊的配置需要注意。我们还已将这种方式与前一篇文章结合起来,出现404状态码时直接抛出
NoHandlerFoundException异常,然后统一拦截处理。
综合本篇文章与之前的《SpringBoot全局异常处理》,只是为大家提供了处理异常以及错误页面的思路,大家可以自由发挥进行组合扩展,下面附上一份针对前一篇文章中的方案二中的
AbstractHandlerExceptionResolver抽象类,增加
NoHandlerFoundException异常的处理。
package com.jianggujin.hr.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; /** * 异常解析 * * @author jianggujin * */ public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver { protected static final Log logger = LogFactory.getLog(AbstractHandlerExceptionResolver.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) { ModelAndView modelView = null; try { onException(e); } catch (Exception e1) { logger.error("ExceptionResolver onException", e1); } try { // 404 处理 if (e instanceof NoHandlerFoundException) { modelView = onPageNotFoundError(request, response, e); return modelView; } } catch (Exception e1) { logger.error("ExceptionResolver onPageNotFoundError", e1); } // 处理器方法,才会执行异常处理 if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; boolean isApi = (method.hasMethodAnnotation(ResponseBody.class) || method.getBeanType().isAnnotationPresent(RestController.class)); try { // api if (isApi) { modelView = onApiError(request, response, e, method); } else { modelView = onPageError(request, response, e, method); } } catch (Exception e1) { logger.error("ExceptionResolver onError", e1); } } return modelView; } /** * 出现异常时回调 * */ protected abstract void onException(Exception e) throws Exception; /** * api请求出错 * */ protected abstract ModelAndView onApiError(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod method) throws Exception; /** * 页面请求出错 * */ protected abstract ModelAndView onPageError(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod method) throws Exception; /** * 页面未找到 * * @param request * @param response * @param e * @return */ protected abstract ModelAndView onPageNotFoundError(HttpServletRequest request, HttpServletResponse response, Exception e); }
我本人比较喜欢这种全局异常处理结合实现
ErrorController的处理方式。
2018年03月02日补充开始
在实际使用中发现如果使用
DeleteMapping请求,当出现错误的时候异常处理会比较诡异,重定向的时候无效,如果禁用
ErrorPageFilter,出现异常的时候会提示
405,所以针对异常,我们还可以通过AOP进行处理,这里针对
DeleteMapping注解的方法进行特殊的处理,以下示例仅为DEMO。
package com.jianggujin.hr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.DeleteMapping; import com.jianggujin.hr.model.result.BaseResult; import com.jianggujin.hr.util.ErrorException; /** * 全局切面,处理{@link DeleteMapping}注解方法,出现异常时会很诡异,用切面处理异常 * * @author jianggujin * */ @Aspect @Component @Order(Ordered.HIGHEST_PRECEDENCE + 1) // 如果设置为HIGHEST_PRECEDENCE会报错 public class WebAspect { protected static final Log logger = LogFactory.getLog(WebAspect.class); @Pointcut("@annotation(org.springframework.web.bind.annotation.DeleteMapping)") public void pointcut() { } /** * 环绕通知<br/> * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 */ @Around("pointcut()") public Object doAroundAdvice(ProceedingJoinPoint joinPoint) { BaseResult result = null; try { Object retVal = joinPoint.proceed(); if (retVal instanceof BaseResult) { result = (BaseResult) retVal; } else if (retVal != null) { result = new BaseResult(result); } } catch (Throwable throwable) { if (throwable instanceof ErrorException) { ErrorException customException = (ErrorException) throwable; result = new BaseResult(customException.getError(), customException.getData()); } else { logger.error("app exception", throwable); result = BaseResult.ERROR; } } return result; } }
2018年03月02日补充结束
相关文章推荐
- Spring Boot - 全局异常处理
- SpringBoot系列之三全局异常的捕获处理
- 轻松实现SpringBoot项目异常全局处理
- SpringBoot学习——全局异常处理设置(返回JSON)
- springboot全局异常处理详解
- spring boot 学习--03---web控制层全局异常处理
- SpringBoot学习之全局异常处理设置(返回JSON)
- springBoot注解大全JPA注解springMVC相关注解全局异常处理
- Spring Boot全局异常处理解析
- spring boot学习教程(4):全局异常处理代码demo
- Spring-Boot--日志操作【全局异常捕获消息处理☞日志控制台输出+日志文件记录】
- Spring-Boot(五) 全局异常处理
- Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
- spring boot 全局异常处理及自定义异常类
- SpringBoot入门——局部与全局的异常处理
- Spring-Boot--日志操作【全局异常捕获消息处理☞日志控制台输出+日志文件记录】
- Spring-Boot--日志操作全局异常捕获消息处理☞日志控制台输出+日志文件记录
- springboot-20-全局异常处理
- Spring boot 全局异常处理
- SpringBoot----全局异常处理