javascript
Spring Cloud 如何统一异常处理?写得太好了!
歡迎關注方志朋的博客,回復”666“獲面試寶典
作者:BNDong
鏈接:www.cnblogs.com/bndong/p/10135370.html
前言
在啟動應用時會發現在控制臺打印的日志中出現了兩個路徑為?{[/error]}?的訪問地址,當系統中發送異常錯誤時,Spring Boot 會根據請求方式分別跳轉到以 JSON 格式或以界面顯示的 /error 地址中顯示錯誤信息。
2018-12-18?09:36:24.627??INFO?19040?---?[???????????main]?s.w.s.m.m.a.RequestMappingHandlerMapping?:?Mapped?"{[/error]}"?... 2018-12-18?09:36:24.632??INFO?19040?---?[???????????main]?s.w.s.m.m.a.RequestMappingHandlerMapping?:?Mapped?"{[/error],produces=[text/html]}"?...默認異常處理
使用 AJAX 方式請求時返回的 JSON 格式錯誤信息。
{"timestamp":?"2018-12-18T01:50:51.196+0000","status":?404,"error":?"Not?Found","message":?"No?handler?found?for?GET?/err404","path":?"/err404" }使用瀏覽器請求時返回的錯誤信息界面。
自定義異常處理
引入依賴
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.54</version> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId> </dependency>fastjson?是 JSON 序列化依賴,?spring-boot-starter-freemarker?是一個模板引擎,用于我們設置錯誤輸出模板。
增加配置
#?出現錯誤時,?直接拋出異常(便于異常統一處理,否則捕獲不到404)
spring.mvc.throw-exception-if-no-handler-found=true
#?不要為工程中的資源文件建立映射
spring.resources.add-mappings=false spring:#?出現錯誤時,?直接拋出異常(便于異常統一處理,否則捕獲不到404)mvc:throw-exception-if-no-handler-found:?true#?不要為工程中的資源文件建立映射resources:add-mappings:?false 新建錯誤信息實體
新建自定義異常
/***?自定義異常*/ public?class?BasicException?extends?RuntimeException?{private?static?final?long?serialVersionUID?=?1L;private?int?code?=?0;public?BasicException(int?code,?String?message)?{super(message);this.code?=?code;}public?int?getCode()?{return?this.code;} } /***?業務異常*/ public?class?BusinessException?extends?BasicException?{private?static?final?long?serialVersionUID?=?1L;public?BusinessException(int?code,?String?message)?{super(code,?message);} }BasicException?繼承了?RuntimeException?,并在原有的 Message 基礎上增加了錯誤碼 code 的內容。而?BusinessException?則是在業務中具體使用的自定義異常類,起到了對不同的異常信息進行分類的作用。
新建 error.ftl 模板文件
位置:/src/main/resources/templates/ 用于顯示錯誤信息
<!DOCTYPE?html> <html> <head><meta?name="robots"?content="noindex,nofollow"?/><meta?name="viewport"?content="width=device-width,?initial-scale=1,?user-scalable=no"><style>h2{color:?#4288ce;font-weight:?400;padding:?6px?0;margin:?6px?0?0;font-size:?18px;border-bottom:?1px?solid?#eee;}/*?Exception?Variables?*/.exception-var?table{width:?100%;max-width:?500px;margin:?12px?0;box-sizing:?border-box;table-layout:fixed;word-wrap:break-word;}.exception-var?table?caption{text-align:?left;font-size:?16px;font-weight:?bold;padding:?6px?0;}.exception-var?table?caption?small{font-weight:?300;display:?inline-block;margin-left:?10px;color:?#ccc;}.exception-var?table?tbody{font-size:?13px;font-family:?Consolas,"Liberation?Mono",Courier,"微軟雅黑";}.exception-var?table?td{padding:?0?6px;vertical-align:?top;word-break:?break-all;}.exception-var?table?td:first-child{width:?28%;font-weight:?bold;white-space:?nowrap;}.exception-var?table?td?pre{margin:?0;}</style> </head> <body><div?class="exception-var"><h2>Exception?Datas</h2><table><tbody><tr><td>Code</td><td>${(exception.code)!}</td></tr><tr><td>Time</td><td>${(exception.timestamp?datetime)!}</td></tr><tr><td>Path</td><td>${(exception.path)!}</td></tr><tr><td>Exception</td><td>${(exception.error)!}</td></tr><tr><td>Message</td><td>${(exception.message)!}</td></tr></tbody></table> </div> </body> </html>編寫全局異常控制類
/***?全局異常控制類*/ @ControllerAdvice public?class?GlobalExceptionHandler?{/***?404異常處理*/@ExceptionHandler(value?=?NoHandlerFoundException.class)@ResponseStatus(HttpStatus.NOT_FOUND)public?ModelAndView?errorHandler(HttpServletRequest?request,?NoHandlerFoundException?exception,?HttpServletResponse?response)?{return?commonHandler(request,?response,exception.getClass().getSimpleName(),HttpStatus.NOT_FOUND.value(),exception.getMessage());}/***?405異常處理*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public?ModelAndView?errorHandler(HttpServletRequest?request,?HttpRequestMethodNotSupportedException?exception,?HttpServletResponse?response)?{return?commonHandler(request,?response,exception.getClass().getSimpleName(),HttpStatus.METHOD_NOT_ALLOWED.value(),exception.getMessage());}/***?415異常處理*/@ExceptionHandler(HttpMediaTypeNotSupportedException.class)public?ModelAndView?errorHandler(HttpServletRequest?request,?HttpMediaTypeNotSupportedException?exception,?HttpServletResponse?response)?{return?commonHandler(request,?response,exception.getClass().getSimpleName(),HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),exception.getMessage());}/***?500異常處理*/@ExceptionHandler(value?=?Exception.class)public?ModelAndView?errorHandler?(HttpServletRequest?request,?Exception?exception,?HttpServletResponse?response)?{return?commonHandler(request,?response,exception.getClass().getSimpleName(),HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());}/***?業務異常處理*/@ExceptionHandler(value?=?BasicException.class)private?ModelAndView?errorHandler?(HttpServletRequest?request,?BasicException?exception,?HttpServletResponse?response)?{return?commonHandler(request,?response,exception.getClass().getSimpleName(),exception.getCode(),exception.getMessage());}/***?表單驗證異常處理*/@ExceptionHandler(value?=?BindException.class)@ResponseBodypublic?ExceptionEntity?validExceptionHandler(BindException?exception,?HttpServletRequest?request,?HttpServletResponse?response)?{List<FieldError>?fieldErrors?=?exception.getBindingResult().getFieldErrors();Map<String,String>?errors?=?new?HashMap<>();for?(FieldError?error:fieldErrors)?{errors.put(error.getField(),?error.getDefaultMessage());}ExceptionEntity?entity?=?new?ExceptionEntity();entity.setMessage(JSON.toJSONString(errors));entity.setPath(request.getRequestURI());entity.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());entity.setError(exception.getClass().getSimpleName());response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());return?entity;}/***?異常處理數據處理*/private?ModelAndView?commonHandler?(HttpServletRequest?request,?HttpServletResponse?response,String?error,?int?httpCode,?String?message)?{ExceptionEntity?entity?=?new?ExceptionEntity();entity.setPath(request.getRequestURI());entity.setError(error);entity.setCode(httpCode);entity.setMessage(message);return?determineOutput(request,?response,?entity);}/***?異常輸出處理*/private?ModelAndView?determineOutput(HttpServletRequest?request,?HttpServletResponse?response,?ExceptionEntity?entity)?{if?(!(request.getHeader("accept").contains("application/json")||?(request.getHeader("X-Requested-With")?!=?null?&&?request.getHeader("X-Requested-With").contains("XMLHttpRequest"))))?{ModelAndView?modelAndView?=?new?ModelAndView("error");modelAndView.addObject("exception",?entity);return?modelAndView;}?else?{response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setCharacterEncoding("UTF8");response.setHeader("Content-Type",?"application/json");try?{response.getWriter().write(ResultJsonTools.build(ResponseCodeConstant.SYSTEM_ERROR,ResponseMessageConstant.APP_EXCEPTION,JSONObject.parseObject(JSON.toJSONString(entity))));}?catch?(IOException?e)?{e.printStackTrace();}return?null;}} }@ControllerAdvice
作用于類上,用于標識該類用于處理全局異常。
@ExceptionHandler
作用于方法上,用于對攔截的異常類型進行處理。value 屬性用于指定具體的攔截異常類型,如果有多個 ExceptionHandler 存在,則需要指定不同的 value 類型,由于異常類擁有繼承關系,所以 ExceptionHandler 會首先執行在繼承樹中靠前的異常類型。
BindException
該異常來自于表單驗證框架 Hibernate validation,當字段驗證未通過時會拋出此異常。
編寫測試 Controller
@RestController public?class?TestController?{@RequestMapping(value?=?"err")public?void?error(){throw?new?BusinessException(400,?"業務異常錯誤信息");}@RequestMapping(value?=?"err2")public?void?error2(){throw?new?NullPointerException("手動拋出異常信息");}@RequestMapping(value?=?"err3")public?int?error3(){int?a?=?10?/?0;return?a;} }使用 AJAX 方式請求時返回的 JSON 格式錯誤信息。
#?/err {"msg":?"應用程序異常","code":?-1,"status_code":?0,"data":?{"path":?"/err","code":?400,"error":?"BusinessException","message":?"業務異常錯誤信息","timestamp":?"2018-12-18?11:09:00"} }#?/err2 {"msg":?"應用程序異常","code":?-1,"status_code":?0,"data":?{"path":?"/err2","code":?500,"error":?"NullPointerException","message":?"手動拋出異常信息","timestamp":?"2018-12-18?11:15:15"} }#?/err3 {"msg":?"應用程序異常","code":?-1,"status_code":?0,"data":?{"path":?"/err3","code":?500,"error":?"ArithmeticException","message":?"/?by?zero","timestamp":?"2018-12-18?11:15:46"} }#?/err404 {"msg":?"應用程序異常","code":?-1,"status_code":?0,"data":?{"path":?"/err404","code":?404,"error":?"NoHandlerFoundException","message":?"No?handler?found?for?GET?/err404","timestamp":?"2018-12-18?11:16:11"} }使用瀏覽器請求時返回的錯誤信息界面。
示例代碼:https://github.com/BNDong/spring-cloud-examples/tree/master/spring-cloud-zuul/cloud-zuul
參考資料:
《微服務 分布式架構開發實戰》 龔鵬 著
https://www.jianshu.com/p/1a49fa436623
重磅!Spring Cloud 生態再添新套件:Spring Cloud Tencent
程序員坐牢了,會被安排去寫代碼嗎?
重磅:某國產IDE發布,稱完全可替代 IntelliJ IDEA,由阿里頭制作!
isEmpty 和 isBlank 的用法區別,至少一半的人答不上來...
勁爆!Java 通用泛型要來了。。
程序員新人上午使用 isXxx 形式定義布爾類型,下午就被勸退?
明天見(。・ω・。)ノ
總結
以上是生活随笔為你收集整理的Spring Cloud 如何统一异常处理?写得太好了!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吉吉生词
- 下一篇: 【课程作业】西瓜书 机器学习课后习题 :