SpringBoot系列(十)統一異常處理與統一結果返回
往期推薦
SpringBoot系列(一)idea新建Springboot項目
Springboot系列(七) 集成接口文檔swagger,使用,測試
SpringBoot系列(八)分分鍾學會Springboot多種解決跨域方式
目錄
引言:
日常開發過程中,難免有的程序會因為某些原因拋出異常,而這些異常一般都是利用try ,catch的方式處理異常或者throw,throws的方式拋出異常不管。這種方法對於程序員來說處理也比較麻煩,對客戶來說也不太友好,所以我們希望既能方便程序員編寫代碼,不用過多的自己去處理各種異常編寫重復的代碼又能提升用戶的體驗,這時候全局異常處理就顯得很重要也很便捷了,是一種不錯的選擇。
1. 全局異常捕獲與處理
因為現在主流的都是前后端分離的項目,所以我們的異常處理也根據前后端分離來講述。
Springboot對於異常的處理也做了不錯的支持,它提供了一個 @ControllerAdvice注解以及 @ExceptionHandler注解,前者是用來開啟全局的異常捕獲,后者則是說明捕獲哪些異常,對那些異常進行處理。
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(value =Exception.class) public String exceptionHandler(Exception e){ System.out.println("發生了一個異常"+e); return e.getMessage(); } }
上面這段代碼就是說,只要是代碼運行過程中有異常就會進行捕獲,並輸出出這個異常。然后我們隨便編寫一個會發生異常的代碼,測試出來的異常是這樣的。
這對於我們前后端分離來說並不好,前后端分離之后唯一的交互就是json了,我們也希望將后端的異常變成json返回給前端處理。下面我們看看統一結果處理。
2. 統一結果返回與統一異常
代碼:
public class Result<T> { //是否成功 private Boolean success; //狀態碼 private Integer code; //提示信息 private String msg; //數據 private T data; public Result() { } //自定義返回結果的構造方法 public Result(Boolean success,Integer code, String msg,T data) { this.success = success; this.code = code; this.msg = msg; this.data = data; } //自定義異常返回的結果 public static Result defineError(DefinitionException de){ Result result = new Result(); result.setSuccess(false); result.setCode(de.getErrorCode()); result.setMsg(de.getErrorMsg()); result.setData(null); return result; } //其他異常處理方法返回的結果 public static Result otherError(ErrorEnum errorEnum){ Result result = new Result(); result.setMsg(errorEnum.getErrorMsg()); result.setCode(errorEnum.getErrorCode()); result.setSuccess(false); result.setData(null); return result; } }
說明:其中省略了get,set方法。另外方法之中包含了一個自定義的枚舉。代碼如下:
public enum ErrorEnum { // 數據操作錯誤定義 SUCCESS(200, "nice"), NO_PERMISSION(403,"你沒得權限"), NO_AUTH(401,"你能不能先登錄一下"), NOT_FOUND(404, "未找到該資源!"), INTERNAL_SERVER_ERROR(500, "服務器跑路了"), ; /** 錯誤碼 */ private Integer errorCode; /** 錯誤信息 */ private String errorMsg; ErrorEnum(Integer errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; } public Integer getErrorCode() { return errorCode; } public String getErrorMsg() { return errorMsg; } }
說明:枚舉類中定義了常見的錯誤碼以及錯誤的提示信息。這里我們就定義好了統一的結果返回,其中里面的靜態方法是用來當程序異常的時候轉換成異常返回規定的格式。
然后我們需要自定義異常處理類。代碼如下:
public class DefinitionException extends RuntimeException{ protected Integer errorCode; protected String errorMsg; public DefinitionException(){ } public DefinitionException(Integer errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; } public Integer getErrorCode() { return errorCode; } public void setErrorCode(Integer errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
其中包含了錯誤的狀態碼,錯誤的提示信息。然后我們可以自定義一個全局異常處理類,來處理各種異常,包括自己定義的異常和內部異常。這樣可以簡化不少代碼,不用自己對每個異常都使用try,catch的方式來實現。
@ControllerAdvice public class GlobalExceptionHandler { /** * 處理自定義異常 * */ @ExceptionHandler(value = DefinitionException.class) @ResponseBody public Result bizExceptionHandler(DefinitionException e) { return Result.defineError(e); } /** * 處理其他異常 * */ @ExceptionHandler(value = Exception.class) @ResponseBody public Result exceptionHandler( Exception e) { return Result.otherError(ErrorEnum.INTERNAL_SERVER_ERROR); } }
說明:每個方法上面加上一個 @ResponseBody的注解,用於將對象解析成json,方便前后端的交互,也可以使用 @ResponseBody放在異常類上面。
3. controller代碼測試與結果
controller代碼:
@RestController @RequestMapping("/result") public class ResultController { @GetMapping("/getStudent") public Result getStudent(){ Student student = new Student(); student.setAge(21); student.setId(111); student.setName("學習筆記"); Result result = new Result(); result.setCode(200); result.setSuccess(true); result.setData(student); result.setMsg("學生列表信息"); return result; } @RequestMapping("/getDeException") public Result DeException(){ throw new DefinitionException(400,"我出錯了"); } @RequestMapping("/getException") public Result Exception(){ Result result = new Result(); int a=1/0; return result; } }
其中的Student類就是前面一直在用的類了。包含三個屬性。其中省略了get,set方法。
public class Student { /** * 唯一標識id */ private Integer id; /** * 姓名 */ private String name; /** * 年齡 */ private Integer age; }
然后啟動項目,來挨個測試。首先測試正常沒有異常發生的數據。瀏覽器輸入:localhost:8095/result/getStudent
可以看到數據是正常返回json串。沒有異常。然后我們測試第二個自定義異常處理接口。瀏覽器輸入localhost:8095/result/getDeException。
可以看到這個自定義的異常是捕獲到了,並且返回了一個json串。最后我們測試一下其他的異常。瀏覽器輸入:localhost:8095/result/getException
到這里我們就處理完了異常並且正確的返回了前端。
這里說一下,測試接口又很多方法,可以使用postman,或者idea自帶的接口測試工具都很好用。
但是,你可能會發現一個問題,這種方法是不能處理404異常的,捕獲不到。該怎么辦呢?
4. 404異常特殊處理。
默認情況下,SpringBoot是不會拋出404異常的,所以@ControllerAdvice也不能捕獲到404異常。我們可以通過以下配置來讓這個注解能捕獲到404異常。
spring.mvc.throw-exception-if-no-handler-found=true spring.resources.add-mappings=false
其中第一句是表示:當發現404異常時直接拋出異常。第二句關閉默認的靜態資源路徑映射。這樣404錯誤也能被捕獲到,但是這個配置會讓你的靜態資源訪問出現問題,也就是不適合前后端不分離的情況。
但是我們可以加上如下配置,就能正常訪問靜態資源了。
@Configuration public class ResourceConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //可以訪問localhost:8095/static/images/image.jpg registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); } }
5. 總結
本文講解了如何處理捕獲全局異常以及怎么自定義異常,順便說明了統一結果的返回格式,並特殊處理的404,not found的異常,將其作為統一結果返回。如果你覺得本文有用,點個贊吧!