1. 首页
  2. IT资讯

Spring Boot 全局异常处理整理

“u003Cpu003E优质文章,及时送达u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRcsUnd92eoboDR” img_width=”640″ img_height=”29″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E作者 | 嘟嘟MDu003Cu002Fpu003Eu003Cpu003E来源 | tengj.topu002F2018u002F05u002F16u002Fspringboot13u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E前言u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E今天来一起学习一下Spring Boot中的异常处理,在日常web开发中发生了异常,往往是需要通过一个统一的异常处理来保证客户端能够收到友好的提示。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E正文u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E本篇要点如下:u003Cu002Fpu003Eu003Cpu003E介绍Spring Boot默认的异常处理机制u003Cu002Fpu003Eu003Cpu003E如何自定义错误页面u003Cu002Fpu003Eu003Cpu003E通过@ControllerAdvice注解来处理异常u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E介绍Spring Boot默认的异常处理机制u003Cu002Fh1u003Eu003Cpu003E默认情况下,Spring Boot为两种情况提供了不同的响应方式。u003Cu002Fpu003Eu003Cpu003E一种是浏览器客户端请求一个不存在的页面或服务端处理发生异常时,一般情况下浏览器默认发送的请求头中Accept: textu002Fhtml,所以Spring Boot默认会响应一个html文档内容,称作“Whitelabel Error Page”u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTdlAzTkxpV” img_width=”1080″ img_height=”287″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E另一种是使用Postman等调试工具发送请求一个不存在的url或服务端处理发生异常时,Spring Boot会返回类似如下的Json格式字符串信息u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E{u003Cbru003E”timestamp”: “2018-05-12T06:11:45.209+0000″,u003Cbru003E”status”: 404,u003Cbru003E”error”: “Not Found”,u003Cbru003E”message”: “No message available”,u003Cbru003E”path”: “u002Findex.html”u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E原理也很简单,Spring Boot 默认提供了程序出错的结果映射路径u002Ferror。这个u002Ferror请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为textu002Fhtml来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:textu002Fhtml)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。u003Cu002Fpu003Eu003Cpu003E相关BasicErrorController中代码如下:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTdy6KrTIXO” img_width=”1080″ img_height=”468″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E如何自定义错误页面u003Cu002Fh1u003Eu003Cpu003E好了,了解完Spring Boot默认的错误机制后,我们来点有意思的,浏览器端访问的话,任何错误Spring Boot返回的都是一个u003Ccodeu003EWhitelabel Error Pageu003Cu002Fcodeu003E的错误页面,这个很不友好,所以我们可以自定义下错误页面。u003Cu002Fpu003Eu003Cpu003E1、先从最简单的开始,直接在u003Ccodeu003Eu002Fresourcesu002Ftemplatesu003Cu002Fcodeu003E下面创建error.html就可以覆盖默认的u003Ccodeu003EWhitelabel Error Pageu003Cu002Fcodeu003E的错误页面,我项目用的是thymeleaf模板,对应的error.html代码如下:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTe8Bv8wSq4″ img_width=”396″ img_height=”286″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpreu003Eu003Ccodeu003E<!DOCTYPE html>u003Cbru003E<html xmlns:th=”http:u002Fu002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Ewwwu003Cu002Fiu003E.thymeleaf.org”>u003Cbru003E<head>u003Cbru003E<meta charset=”UTF-8″>u003Cbru003E<title>Title<u002Ftitle>u003Cbru003E<u002Fhead>u003Cbru003E<body>u003Cbru003E动态error错误页面u003Cbru003E<p th:text=”${error}”><u002Fp>u003Cbru003E<p th:text=”${status}”><u002Fp>u003Cbru003E<p th:text=”${message}”><u002Fp>u003Cbru003E<u002Fbody>u003Cbru003E<u002Fhtml>u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E这样运行的时候,请求一个不存在的页面或服务端处理发生异常时,展示的自定义错误界面如下:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTeG9pLLz14″ img_width=”668″ img_height=”388″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E2、此外,如果你想更精细一点,根据不同的状态码返回不同的视图页面,也就是对应的404,500等页面,这里分两种,错误页面可以是静态HTML(即,添加到任何静态资源文件夹下),也可以使用模板构建,文件的名称应该是确切的状态码。u003Cu002Fpu003Eu003Cpu003E如果只是静态HTML页面,不带错误信息的,在resourcesu002Fpublicu002F下面创建error目录,在error目录下面创建对应的状态码html即可 ,例如,要将404映射到静态HTML文件,您的文件夹结构如下所示:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTeOC3E920Y” img_width=”526″ img_height=”284″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E静态404.html简单页面如下:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E<!DOCTYPE html>u003Cbru003E<html lang=”en”>u003Cbru003E<head>u003Cbru003E<meta charset=”UTF-8″>u003Cbru003E<title>Title<u002Ftitle>u003Cbru003E<u002Fhead>u003Cbru003E<body>u003Cbru003E静态404错误页面u003Cbru003E<u002Fbody>u003Cbru003E<u002Fhtml>u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E这样访问一个错误路径的时候,就会显示u003Ccodeu003E静态404错误页面u003Cu002Fcodeu003E错误页面u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTu3ArNbcrc” img_width=”610″ img_height=”156″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cblockquote toutiao-origin=”blockquote” class=”pgc-blockquote-abstract”u003Eu003Cpu003E注:这时候如果存在上面第一种介绍的error.html页面,则状态码错误页面将覆盖error.html,具体状态码错误页面优先级比较高。u003Cu002Fpu003Eu003Cbru003Eu003Cu002Fblockquoteu003Eu003Cpu003E如果是动态模板页面,可以带上错误信息,在u003Ccodeu003Eresourcesu002Ftemplatesu002Fu003Cu002Fcodeu003E下面创建error目录,在error目录下面命名即可:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTuL18webnY” img_width=”554″ img_height=”358″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E这里我们模拟下500错误,控制层代码,模拟一个除0的错误:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@Controller u003Cbru003Epublicu003Cstrong toutiao-origin=”span”u003Eclassu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBaseErrorControlleru003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eextendsu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EAbstractControlleru003Cu002Fstrongu003E{u003Cbru003Eprivate Logger logger = LoggerFactory.getLogger(this.getClass);u003Cbru003Eu003Cbru003E@RequestMapping(value=”u002Fex”)u003Cbru003E@ResponseBodyu003Cbru003Epublic String error{u003Cbru003Eint i=5u002F0;u003Cbru003Ereturn “ex”;u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E500.html代码:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E<!DOCTYPE html> u003Cbru003E<html xmlns:th=”http:u002Fu002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Ewwwu003Cu002Fiu003E.thymeleaf.org”>u003Cbru003E<head>u003Cbru003E<meta charset=”UTF-8″>u003Cbru003E<title>Title<u002Ftitle>u003Cbru003E<u002Fhead>u003Cbru003E<body>u003Cbru003E动态500错误页面u003Cbru003E<p th:text=”${error}”><u002Fp>u003Cbru003E<p th:text=”${status}”><u002Fp>u003Cbru003E<p th:text=”${message}”><u002Fp>u003Cbru003E<u002Fbody>u003Cbru003E<u002Fhtml>u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E这时访问 http:u002Fu002Flocalhost:8080u002Fspringu002Fex 即可看到如下错误,说明确实映射到了500.htmlu003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTuTIk177WX” img_width=”602″ img_height=”370″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cblockquoteu003Eu003Cpu003E注:如果同时存在静态页面500.html和动态模板的500.html,则后者覆盖前者。即u003Ccodeu003Etemplatesu002Ferroru002Fu003Cu002Fcodeu003E这个的优先级比u003Ccodeu003Eresourcesu002Fpublicu002Ferroru003Cu002Fcodeu003E高。u003Cu002Fpu003Eu003Cbru003Eu003Cu002Fblockquoteu003Eu003Cpu003E整体概括上面几种情况,如下:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Eerror.html会覆盖默认的 whitelabel Error Page 错误提示u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E静态错误页面优先级别比error.html高u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E动态模板错误页面优先级比静态错误页面高u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E3、上面介绍的只是最简单的覆盖错误页面的方式来自定义,如果对于某些错误你可能想特殊对待,则可以这样u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@Configuration u003Cbru003Epublicu003Cstrong toutiao-origin=”span”u003Eclassu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EContainerConfigu003Cu002Fstrongu003E{u003Cbru003E@Beanu003Cbru003Epublic Embeu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EedServletContainerCustomizer containerCustomizer{u003Cbru003Ereturn new Embeu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EedServletContainerCustomizer{u003Cbru003E@Overrideu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Evoidu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Ecustomizeu003Cu002Fstrongu003E(ConfigurableEmbeu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EedServletContainer container) {u003Cbru003Econtainer.au003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, “u002Ferroru002F500”));u003Cbru003E}u003Cbru003E};u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E上面这段代码中u003Ccodeu003EHttpStatus.INTERNAL_SERVER_ERRORu003Cu002Fcodeu003E就是对应500错误码,也就是说程序如果发生500错误,就会将请求转发到u003Ccodeu003Eu002Ferroru002F500u003Cu002Fcodeu003E这个映射来,那我们只要实现一个方法是对应这个u003Ccodeu003Eu002Ferroru002F500u003Cu002Fcodeu003E映射即可捕获这个异常做出处理u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@RequestMapping(“u002Ferroru002F500”)u003Cbru003E@ResponseBodyu003Cbru003Epublic String showServerError {u003Cbru003Ereturn “server error”;u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E这样,我们再请求前面提到的异常请求 http:u002Fu002Flocalhost:8080u002Fspringu002Fex 的时候,就会被我们这个方法捕获了。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTuc939pFY1″ img_width=”596″ img_height=”148″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E这里我们就只对500做了特殊处理,并且返还的是字符串,如果想要返回视图,去掉 @ResponseBody注解,并返回对应的视图页面。如果想要对其他状态码自定义映射,在customize方法中添加即可。u003Cu002Fpu003Eu003Cpu003E上面这种方法虽然我们重写了u002F500映射,但是有一个问题就是无法获取错误信息,想获取错误信息的话,我们可以继承BasicErrorController或者干脆自己实现ErrorController接口,除了用来响应u002Ferror这个错误页面请求,可以提供更多类型的错误格式等(BasicErrorController在上面介绍SpringBoot默认异常机制的时候有提到)u003Cu002Fpu003Eu003Cpu003E这里博主选择直接继承BasicErrorController,然后把上面 u003Ccodeu003Eu002Ferroru002F500u003Cu002Fcodeu003E映射方法添加进来即可u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@Controlleru003Cbru003Epublic class MyBasicErrorController extends BasicErrorController {u003Cbru003Eu003Cbru003Epublic MyBasicErrorController {u003Cbru003Esuper(new DefaultErrorAttributes, new ErrorProperties);u003Cbru003E}u003Cbru003Eu003Cbru003Eu002F**u003Cbru003E* 定义500的ModelAndViewu003Cbru003E* @param requestu003Cbru003E* @param responseu003Cbru003E* @returnu003Cbru003E*u002Fu003Cbru003Eu003Cbru003E@RequestMapping(produces = “textu002Fhtml”,value = “u002F500”)u003Cbru003Epublic ModelAndView errorHtml500(HttpServletRequest request,HttpServletResponse response) {u003Cbru003Eresponse.setStatus(getStatus(request).value);u003Cbru003EMap<String, Object> model = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));u003Cbru003Emodel.put(“msg”,”自定义错误信息”);u003Cbru003Ereturn new ModelAndView(“erroru002F500”, model);u003Cbru003E}u003Cbru003Eu003Cbru003Eu002F**u003Cbru003E* 定义500的错误JSON信息u003Cbru003E* @param requestu003Cbru003E* @returnu003Cbru003E*u002Fu003Cbru003Eu003Cbru003E@RequestMapping(value = “u002F500”)u003Cbru003E@ResponseBodyu003Cbru003Eu003Cbru003Epublic ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {u003Cbru003EMap<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));u003Cbru003EHttpStatus status = getStatus(request);u003Cbru003Ereturn new ResponseEntity<Map<String, Object>>(body, status);u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E代码也很简单,只是实现了自定义的500错误的映射解析,分别对浏览器请求以及json请求做了回应。u003Cu002Fpu003Eu003Cpu003EBasicErrorController默认对应的@RequestMapping是u003Ccodeu003Eu002Ferroru003Cu002Fcodeu003E,固我们方法里面对应的u003Ccodeu003E@RequestMapping(produces = “textu002Fhtml”,value = “u002F500″)u003Cu002Fcodeu003E实际上完整的映射请求是u003Ccodeu003Eu002Ferroru002F500u003Cu002Fcodeu003E,这就跟上面 customize 方法自定义的映射路径对上了。u003Cu002Fpu003Eu003Cpu003EerrorHtml500 方法中,我返回的是模板页面,对应u002Ftemplatesu002Ferroru002F500.html,这里顺便自定义了一个msg信息,在500.html也输出这个信息u003Ccodeu003E<p th:text=”${msg}”><u002Fp>u003Cu002Fcodeu003E,如果输出结果有这个信息,则表示我们配置正确了。u003Cu002Fpu003Eu003Cpu003E再次访问请求http:u002Fu002Flocalhost:8080u002Fspringu002Fex ,结果如下u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXTuk78OZ7TV” img_width=”580″ img_height=”442″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003ETips:大家可以u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E关注u003Cu002Fiu003Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E微信u003Cu002Fiu003E公众号:Java后端,获取更多推送。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003E通过@ControllerAdvice注解来处理异常u003Cu002Fh1u003Eu003Cpu003ESpring Boot提供的ErrorController是一种全局性的容错机制。此外,你还可以用@ControllerAdvice注解和@ExceptionHandler注解实现对指定异常的特殊处理。u003Cu002Fpu003Eu003Cpu003E这里介绍两种情况:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E局部异常处理 @Controller + @ExceptionHandleru003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E全局异常处理 @ControllerAdvice + @ExceptionHandleru003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span”u003E局部异常处理 @Controller + @ExceptionHandleru003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E局部异常主要用到的是@ExceptionHandler注解,此注解注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在全局。u003Cu002Fpu003Eu003Cpu003E该注解用于标注处理方法处理那些特定的异常。被该注解标注的方法可以有以下任意顺序的参数类型:u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003EThrowable、Exception 等异常对象;u003Cu002Fpu003Eu003Cpu003EServletRequest、HttpServletRequest、ServletResponse、HttpServletResponse;u003Cu002Fpu003Eu003Cpu003EHttpSession 等会话对象;u003Cu002Fpu003Eu003Cpu003Eorg.springframework.web.context.request.WebRequest;u003Cu002Fpu003Eu003Cpu003Ejava.util.Locale;u003Cu002Fpu003Eu003Cpu003Ejava.io.InputStream、java.io.Reader;u003Cu002Fpu003Eu003Cpu003Ejava.io.OutputStream、java.io.Writer;u003Cu002Fpu003Eu003Cpu003Eorg.springframework.ui.Model;u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpu003E并且被该注解标注的方法可以有以下的返回值类型可选:u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003EModelAndView;u003Cu002Fpu003Eu003Cpu003Eorg.springframework.ui.Model;u003Cu002Fpu003Eu003Cpu003Ejava.util.Map;u003Cu002Fpu003Eu003Cpu003Eorg.springframework.web.servlet.View;u003Cu002Fpu003Eu003Cpu003E@ResponseBody 注解标注的任意对象;u003Cu002Fpu003Eu003Cpu003EHttpEntity<?> or ResponseEntity<?>;u003Cu002Fpu003Eu003Cpu003Evoid;u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpu003E以上罗列的不完全,更加详细的信息可参考:Spring ExceptionHandler。u003Cu002Fpu003Eu003Cpu003E举个简单例子,这里我们对除0异常用@ExceptionHandler来捕捉。u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@Controlleru003Cbru003Epublicu003Cstrong toutiao-origin=”span”u003Eclassu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBaseErrorControlleru003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eextendsu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EAbstractControlleru003Cu002Fstrongu003E{u003Cbru003Eprivate Logger logger = LoggerFactory.getLogger(this.getClass);u003Cbru003Eu003Cbru003E@RequestMapping(value=”u002Fex”)u003Cbru003E@ResponseBodyu003Cbru003Epublic String error{u003Cbru003Eint i=5u002F0;u003Cbru003Ereturn “ex”;u003Cbru003E}u003Cbru003Eu003Cbru003Eu002Fu002F局部异常处理u003Cbru003E@ExceptionHandler(Exception.class)u003Cbru003E@ResponseBodyu003Cbru003Epublic String exHandler(Exception e){u003Cbru003Eu002Fu002F 判断发生异常的类型是除0异常则做出响应u003Cbru003Eif(e instanceof ArithmeticException){u003Cbru003Ereturn “发生了除0异常”;u003Cbru003E}u003Cbru003Eu002Fu002F 未知的异常做出响应u003Cbru003Ereturn “发生了未知异常”;u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXUBq8YXdIFS” img_width=”648″ img_height=”152″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E全局异常处理 @ControllerAdvice + @ExceptionHandleru003Cu002Fpu003Eu003Cpu003E在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。u003Cu002Fpu003Eu003Cpu003E简单的说,进入Controller层的错误才会由@ControllerAdvice处理,拦截器抛出的错误以及访问错误地址的情况@ControllerAdvice处理不了,由SpringBoot默认的异常处理机制处理。u003Cu002Fpu003Eu003Cpu003E我们实际开发中,如果是要实现RESTful API,那么默认的JSON错误信息就不是我们想要的,这时候就需要统u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E一一u003Cu002Fiu003E下JSON格式,所以需要封装一下。u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003Eu002F**u003Cbru003E* 返回数据u003Cbru003E*u002Fu003Cbru003Epublic class AjaxObject extends HashMap<String, Object> {u003Cbru003Eprivate static final long serialVersionUID = 1L;u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EAjaxObjectu003Cu002Fstrongu003E {u003Cbru003Eput(“code”, 0);u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject error {u003Cbru003Ereturn error(HttpStatus.SC_INTERNAL_SERVER_ERROR, “未知异常,请联系管理员”);u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject error(String msg) {u003Cbru003Ereturn error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject error(int code, String msg) {u003Cbru003EAjaxObject r = new AjaxObject;u003Cbru003Er.put(“code”, code);u003Cbru003Er.put(“msg”, msg);u003Cbru003Ereturn r;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject ok(String msg) {u003Cbru003EAjaxObject r = new AjaxObject;u003Cbru003Er.put(“msg”, msg);u003Cbru003Ereturn r;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject ok(Map<String, Object> map) {u003Cbru003EAjaxObject r = new AjaxObject;u003Cbru003Er.putAll(map);u003Cbru003Ereturn r;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject ok {u003Cbru003Ereturn new AjaxObject;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic AjaxObject put(String key, Object value) {u003Cbru003Esuper.put(key, value);u003Cbru003Ereturn this;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic AjaxObject data(Object value) {u003Cbru003Esuper.put(“data”, value);u003Cbru003Ereturn this;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic static AjaxObject apiError(String msg) {u003Cbru003Ereturn error(1, msg);u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E上面这个AjaxObject就是我平时用的,如果是正确情况返回的就是:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E{u003Cbru003Ecode:0,u003Cbru003Emsg:“获取列表成功”,u003Cbru003Edata:{u003Cbru003EqueryList :u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E正确默认code返回0,data里面可以是集合,也可以是对象,如果是异常情况,返回的json则是:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E{u003Cbru003Ecode:500,u003Cbru003Emsg:“未知异常,请联系管理员”u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E然后创建一个自定义的异常类:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003Epublic u003Cstrong toutiao-origin=”span”u003Eclassu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eextendsu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003ERuntimeExceptionu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eimplementsu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003ESerializableu003Cu002Fstrongu003E{u003Cbru003Eu003Cbru003Eprivate static final long serialVersionUID = 1L;u003Cbru003Eprivate String msg;u003Cbru003Eprivate int code = 500;u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionu003Cu002Fstrongu003E(String msg) {u003Cbru003Esuper(msg);u003Cbru003Ethis.msg = msg;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionu003Cu002Fstrongu003E(String msg, Throwable e) {u003Cbru003Esuper(msg, e);u003Cbru003Ethis.msg = msg;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionu003Cu002Fstrongu003E(int code,String msg) {u003Cbru003Esuper(msg);u003Cbru003Ethis.msg = msg;u003Cbru003Ethis.code = code;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionu003Cu002Fstrongu003E(String msg, int code, Throwable e) {u003Cbru003Esuper(msg, e);u003Cbru003Ethis.msg = msg;u003Cbru003Ethis.code = code;u003Cbru003E}u003Cbru003Eu003Cbru003Epublic String getMsg {u003Cbru003Ereturn msg;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Evoidu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EsetMsgu003Cu002Fstrongu003E(String msg) {u003Cbru003Ethis.msg = msg;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eintu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EgetCodeu003Cu002Fstrongu003E {u003Cbru003Ereturn code;u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Evoidu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EsetCodeu003Cu002Fstrongu003E(int code) {u003Cbru003Ethis.code = code;u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E注:spring 对于 RuntimeException 异常才会进行事务回滚u003Cu002Fpu003Eu003Cpu003EControler中添加一个json映射,用来处理这个异常u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E@Controlleru003Cbru003Epublic class BaseErrorController{u003Cbru003E@RequestMapping(“u002Fjson”)u003Cbru003Epublic void json(ModelMap modelMap) {u003Cbru003ESystem.out.println(modelMap.get(“author”));u003Cbru003Eint i=5u002F0;u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E最后创建这个全局异常处理类:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003Eu002F**u003Cbru003E* 异常处理器u003Cbru003E*u002Fu003Cbru003E@RestControllerAdviceu003Cbru003Epublicu003Cstrong toutiao-origin=”span”u003Eclassu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EBusinessExceptionHandleru003Cu002Fstrongu003E{u003Cbru003Eprivate Logger logger = LoggerFactory.getLogger(getClass);u003Cbru003Eu003Cbru003Eu003Cbru003Eu003Cbru003Eu002F**u003Cbru003E* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器u003Cbru003E* @param binderu003Cbru003E*u002Fu003Cbru003E@InitBinderu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Evoidu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003EinitBinderu003Cu002Fstrongu003E(WebDataBinder binder) {u003Cbru003ESystem.out.println(“请求有参数才进来”);u003Cbru003E}u003Cbru003Eu003Cbru003Eu002F**u003Cbru003E* 把值绑定到Model中,使全局@RequestMapping可以获取到该值u003Cbru003E* @param modelu003Cbru003E*u002Fu003Cbru003E@ModelAttributeu003Cbru003Eu003Cstrong toutiao-origin=”span”u003Epublicu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Evoidu003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”span”u003Eau003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EAttributesu003Cu002Fstrongu003E(Model model) {u003Cbru003Emodel.au003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EAttribute(“author”, “嘟嘟MD”);u003Cbru003E}u003Cbru003Eu003Cbru003E@ExceptionHandler(Exception.class)u003Cbru003Epublic Object handleException(Exception e,HttpServletRequest req){u003Cbru003EAjaxObject r = new AjaxObject;u003Cbru003Eu002Fu002F业务异常u003Cbru003Eif(e instanceof BusinessException){u003Cbru003Er.put(“code”, ((BusinessException) e).getCode);u003Cbru003Er.put(“msg”, ((BusinessException) e).getMsg);u003Cbru003E}else{u002Fu002F系统异常u003Cbru003Er.put(“code”,”500″);u003Cbru003Er.put(“msg”,”未知异常,请联系管理员”);u003Cbru003E}u003Cbru003Eu003Cbru003Eu002Fu002F使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)u003Cbru003EString contentTypeHeader = req.getHeader(“Content-Type”);u003Cbru003EString acceptHeader = req.getHeader(“Accept”);u003Cbru003EString xRequestedWith = req.getHeader(“X-Requested-With”);u003Cbru003Eif ((contentTypeHeader != && contentTypeHeader.contains(“applicationu002Fjson”))u003Cbru003E|| (acceptHeader != && acceptHeader.contains(“applicationu002Fjson”))u003Cbru003E|| “XMLHttpRequest”.equalsIgnoreCase(xRequestedWith)) {u003Cbru003Ereturn r;u003Cbru003E} else {u003Cbru003EModelAndView modelAndView = new ModelAndView;u003Cbru003EmodelAndView.au003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EObject(“msg”, e.getMessage);u003Cbru003EmodelAndView.au003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EObject(“url”, req.getRequestURL);u003Cbru003EmodelAndView.au003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Eddu003Cu002Fiu003EObject(“stackTrace”, e.getStackTrace);u003Cbru003EmodelAndView.setViewName(“error”);u003Cbru003Ereturn modelAndView;u003Cbru003E}u003Cbru003E}u003Cbru003E}u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E@ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,上面我配置了拦截Exception,u003Cu002Fpu003Eu003Cpu003E再根据不同异常类型返回不同的相应,最后添加判断,如果是Ajax请求,则返回json,如果是非ajax则返回view,这里是返回到error.html页面。u003Cu002Fpu003Eu003Cpu003E为了展示错误的时候更友好,我封装了下error.html,不仅展示了错误,还添加了跳转百度谷歌以及StackOverFlow的按钮,如下:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E<!DOCTYPE HTML>u003Cbru003E<html xmlns:th=”http:u002Fu002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Ewwwu003Cu002Fiu003E.thymeleaf.org” layout:decorator=”layout”>u003Cbru003E<head>u003Cbru003E<title>Spring Boot管理后台<u002Ftitle>u003Cbru003E<script type=”textu002Fjavascript”>u003Cbru003E<u002Fscript>u003Cbru003E<u002Fhead>u003Cbru003E<body>u003Cbru003E<div layout:fragment=”content” th:remove=”tag”>u003Cbru003E<div id=”navbar”>u003Cbru003E<h1>系统异常统一处理<u002Fh1>u003Cbru003E<h3 th:text=”‘错误信息:’+${msg}”><u002Fh3>u003Cbru003E<h3 th:text=”‘请求地址:’+${url}”><u002Fh3>u003Cbru003Eu003Cbru003E<h2>Debug<u002Fh2>u003Cbru003E<a th:href=”@{‘https:u002Fu002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Ewwwu003Cu002Fiu003E.googleu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E.comu003Cu002Fiu003Eu002Fwebhp?hl=zh-CN#safe=strict&hl=zh-CN&q=’+${msg}}”u003Cbru003Eclass=”btn btn-primary btn-lg” target=”_blank” id=”Google”>Google<u002Fa>u003Cbru003E<a th:href=”@{‘https:u002Fu002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003Ewwwu003Cu002Fiu003E.baiduu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E.comu003Cu002Fiu003Eu002Fs?wd=’+${msg}}” class=”btn btn-info btn-lg” target=”_blank” id=”Baidu”>Baidu<u002Fa>u003Cbru003E<a th:href=”@{‘http:u002Fu002Fstackoverflowu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E.comu003Cu002Fiu003Eu002Fsearch?q=’+${msg}}”u003Cbru003Eclass=”btn btn-default btn-lg” target=”_blank” id=”StackOverFlow”>StackOverFlow<u002Fa>u003Cbru003E<h2>异常堆栈跟踪日志StackTrace<u002Fh2>u003Cbru003E<div th:each=”line:${stackTrace}”>u003Cbru003E<div th:text=”${line}”><u002Fdiv>u003Cbru003E<u002Fdiv>u003Cbru003E<u002Fdiv>u003Cbru003E<u002Fdiv>u003Cbru003E<div layout:fragment=”js” th:remove=”tag”>u003Cbru003E<u002Fdiv>u003Cbru003E<u002Fbody>u003Cbru003E<u002Fhtml>u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E访问http:u002Fu002Flocalhost:8080u002Fjson的时候,因为是浏览器发起的,返回的是error界面:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRgvXUBy5OEZPcu” img_width=”1080″ img_height=”736″ alt=”Spring Boot 全局异常处理整理” inline=”0″u003Eu003Cpu003E如果是ajax请求,返回的就是错误:u003Cu002Fpu003Eu003Cpreu003Eu003Ccodeu003E{ “msg”:”未知异常,请联系管理员”, “code”:500 }u003Cu002Fcodeu003Eu003Cu002Fpreu003Eu003Cpu003E这里我给带@ModelAttribute注解的方法通过Model设置了author值,在json映射方法中通过 ModelMwap 获取到改值。u003Cu002Fpu003Eu003Cpu003E认真的你可能发现,全局异常类我用的是@RestControllerAdvice,而不是@ControllerAdvice,因为这里返回的主要是json格式,这样可以少写一个@ResponseBody。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E总结u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E到此,SpringBoot中对异常的使用也差不多全了,本项目中处理异常的顺序会是这样,当发送一个请求:u003Cu002Fpu003Eu003Cpu003E1.拦截器那边先判断是否登录,没有则返回登录页。u003Cu002Fpu003Eu003Cpu003E2.在进入Controller之前,譬如请求一个不存在的地址,返回404错误界面。u003Cu002Fpu003Eu003Cpu003E3.在执行@RequestMapping时,发现的各种错误(譬如数据库报错、请求参数格式错误u002F缺失u002F值非法等)统一由@ControllerAdvice处理,根据是否Ajax返回json或者view。u003Cu002Fpu003Eu003Cp class=”pgc-end-source”u003E-END-u003Cu002Fpu003E”

原文始发于:Spring Boot 全局异常处理整理

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/17800.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code