javascript
Spring Cloud:统一异常处理
在啟動應用時會發現在控制臺打印的日志中出現了兩個路徑為?{[/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?是一個模板引擎,用于我們設置錯誤輸出模板。
增加配置
properties
# 出現錯誤時, 直接拋出異常(便于異常統一處理,否則捕獲不到404) spring.mvc.throw-exception-if-no-handler-found=true # 不要為工程中的資源文件建立映射 spring.resources.add-mappings=falseyml
spring:# 出現錯誤時, 直接拋出異常(便于異常統一處理,否則捕獲不到404)mvc:throw-exception-if-no-handler-found: true# 不要為工程中的資源文件建立映射resources:add-mappings: false新建錯誤信息實體
/*** 信息實體*/ public class ExceptionEntity implements Serializable {private static final long serialVersionUID = 1L;private String message;private int code;private String error;private String path;@JSONField(format = "yyyy-MM-dd hh:mm:ss")private Date timestamp = new Date();public static long getSerialVersionUID() {return serialVersionUID;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getError() {return error;}public void setError(String error) {this.error = error;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public Date getTimestamp() {return timestamp;}public void setTimestamp(Date timestamp) {this.timestamp = timestamp;} }新建自定義異常
/*** 自定義異常*/ 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 calidation,當字段驗證未通過時會拋出此異常。
編寫測試 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
轉載于:https://www.cnblogs.com/bndong/p/10135370.html
總結
以上是生活随笔為你收集整理的Spring Cloud:统一异常处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTF writeup 0_IDF实验室
- 下一篇: MySQL相关语句使用注意点