1. 程式人生 > >SpringBoot | 第八章:統一異常、資料校驗處理

SpringBoot | 第八章:統一異常、資料校驗處理

前言

在web應用中,請求處理時,出現異常是非常常見的。所以當應用出現各類異常時,進行異常的捕獲或者二次處理(比如sql異常正常是不能外拋)是非常必要的,比如在開發對外api服務時,約定了響應的引數格式,如respCoderespMsg,呼叫方根據錯誤碼進行自己的業務邏輯。本章節就重點講解下統一異常和資料校驗處理。

springboot中,預設在傳送異常時,會跳轉值/error請求進行錯誤的展現,根據不同的Content-Type展現不同的錯誤結果,如json請求時,直接返回json格式引數。

瀏覽器訪問異常時:

使用postman訪問時:

統一異常處理

顯然,預設的異常頁是對使用者或者呼叫者而言都是不友好的,所以一般上我們都會進行實現自己業務的異常提示資訊。

建立全域性的統一異常處理類

利用@ControllerAdvice@ExceptionHandler定義一個統一異常處理類

  • @ControllerAdvice:控制器增強,使@ExceptionHandler、@InitBinder、@ModelAttribute註解的方法應用到所有的 @RequestMapping註解的方法。
  • @ExceptionHandler:異常處理器,此註解的作用是當出現其定義的異常時進行處理的方法

建立異常類:CommonExceptionHandler

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@ControllerAdvice

public class CommonExceptionHandler {

/**

*  攔截Exception類的異常

* @param e

* @return

*/

@ExceptionHandler(Exception.class)

@ResponseBody

public Map<String,Object> exceptionHandler(Exception e){

Map<String,Object> result = new HashMap<String,Object>();

result.put("respCode", "9999");

result.put("respMsg", e.getMessage());

//正常開發中,可建立一個統一響應實體,如CommonResp

return result;

}

}

多餘不同異常(如自定義異常),需要進行不同的異常處理時,可編寫多個exceptionHandler方法,註解ExceptionHandler指定處理的異常類,如

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/**

* 攔截 CommonException 的異常

* @param ex

* @return

*/

@ExceptionHandler(CommonException.class)

@ResponseBody

public Map<String,Object> exceptionHandler(CommonException ex){

log.info("CommonException:{}({})",ex.getMsg(), ex.getCode());

Map<String,Object> result = new HashMap<String,Object>();

result.put("respCode", ex.getCode());

result.put("respMsg", ex.getMsg());

return result;

}

由於加入了@ResponseBody,所以返回的是json格式

說明異常已經被攔截了。

可攔截不同的異常,進行不同的異常提示,比如NoHandlerFoundExceptionHttpMediaTypeNotSupportedExceptionAsyncRequestTimeoutException等等,這裡就不列舉了,讀者可自己加入後實際操作下。

對於返回頁面時,返回ModelAndView即可,如

1

2

3

4

5

6

7

8

@ExceptionHandler(value = Exception.class)

public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

ModelAndView mav = new ModelAndView();

mav.addObject("exception", e);

mav.addObject("url", req.getRequestURL());

mav.setViewName(DEFAULT_ERROR_VIEW);

return mav;

}

由於工作中都是才有前後端分離開發模式,所以一般上都沒有直接返回資源頁的需求了,一般上都是返回固定的響應格式,如respCoderespMsgdata,前端通過判斷respCode的值進行業務判斷,是彈窗還是跳轉頁面。

資料校驗

在web開發時,對於請求引數,一般上都需要進行引數合法性校驗的,原先的寫法時一個個欄位一個個去判斷,這種方式太不通用了,所以java的JSR 303: Bean Validation規範就是解決這個問題的。

JSR 303只是個規範,並沒有具體的實現,目前通常都是才有hibernate-validator進行統一引數校驗。

JSR303定義的校驗型別

Constraint 詳細資訊
@Null 被註釋的元素必須為 null
@NotNull 被註釋的元素必須不為 null
@AssertTrue 被註釋的元素必須為 true
@AssertFalse 被註釋的元素必須為 false
@Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min) 被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future 被註釋的元素必須是一個將來的日期
@Pattern(value) 被註釋的元素必須符合指定的正則表示式

Hibernate Validator 附加的 constraint

Constraint 詳細資訊
@Email 被註釋的元素必須是電子郵箱地址
@Length 被註釋的字串的大小必須在指定的範圍內
@NotEmpty 被註釋的字串的必須非空
@Range 被註釋的元素必須在合適的範圍內

建立實體類

1

2

3

4

5

6

7

8

9

10

11

12

@Data

@NoArgsConstructor

@AllArgsConstructor

public class DemoReq {

@NotBlank(message="code不能為空")

String code;

@Length(max=10,message="name長度不能超過10")

String name;

}

然後在控制層方法裡,加入@Valid即可,這樣在訪問前,會對請求引數進行檢驗。

1

2

3

4

@GetMapping("/demo/valid")

public String demoValid(@Valid DemoReq req) {

return req.getCode() + "," + req.getName();

}

啟動,後訪問http://127.0.0.1:8080/demo/valid

加上正確引數後,http://127.0.0.1:8080/demo/valid?code=3&name=s

這樣資料的統一校驗就完成了,對於其他註解的使用,大家可自行谷歌下,基本上都很簡單的,對於已有的註解無法滿足校驗需要時,也可進行自定義註解的開發,一下簡單講解下,自定義註解的編寫

不使用@valid的情況下,也可利用程式設計的方式編寫一個工具類,進行實體引數校驗

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class ValidatorUtil {

private static Validator validator = ((HibernateValidatorConfiguration) Validation

.byProvider(HibernateValidator.class).configure()).failFast(true).buildValidatorFactory().getValidator();

/**

* 實體校驗

*

* @param obj

* @throws CommonException

*/

public static <T> void validate(T obj) throws CommonException {

Set<ConstraintViolation<T>> constraintViolations = validator.validate(obj, new Class[0]);

if (constraintViolations.size() > 0) {

ConstraintViolation<T> validateInfo = (ConstraintViolation<T>) constraintViolations.iterator().next();

// validateInfo.getMessage() 校驗不通過時的資訊,即message對應的值

throw new CommonException("0001", validateInfo.getMessage());

}

}

}

使用

1

2

3

4

5

6

@GetMapping("/demo/valid")

public String demoValid(@Valid DemoReq req) {

//手動校驗

ValidatorUtil.validate(req);

return req.getCode() + "," + req.getName();

}

自定義校驗註解

自定義註解,主要時實現ConstraintValidator的處理類即可,這裡已編寫一個校驗常量的註解為例:引數值只能為特定的值。

自定義註解

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@Documented

//指定註解的處理類

@Constraint(validatedBy = {ConstantValidatorHandler.class })

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })

@Retention(RUNTIME)

public @interface Constant {

String message() default "{constraint.default.const.message}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

String value();

}

註解處理類

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class ConstantValidatorHandler implements ConstraintValidator<Constant, String> {

private String constant;

@Override

public void initialize(Constant constraintAnnotation) {

//獲取設定的欄位值

this.constant = constraintAnnotation.value();

}

@Override

public boolean isValid(String value, ConstraintValidatorContext context) {

//判斷引數是否等於設定的欄位值,返回結果

return constant.equals(value);

}

}

使用

1

2

@Constant(message = "verson只能為1.0",value="1.0")

String version;

執行:

此時,自定義註解已生效。大家可根據實際需求進行開發。

大家看到在校驗不通過時,返回的異常資訊是不友好的,此時可利用統一異常處理,對校驗異常進行特殊處理,特別說明下,對於異常處理類,共有以下幾種情況(被@RequestBody和@RequestParam註解的請求實體,校驗異常類是不同的)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

@ExceptionHandler(MethodArgumentNotValidException.class)

public Map<String,Object> handleBindException(MethodArgumentNotValidException ex) {

FieldError fieldError = ex.getBindingResult().getFieldError();

log.info("引數校驗異常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());

Map<String,Object> result = new HashMap<String,Object>();

result.put("respCode", "01002");

result.put("respMsg", fieldError.getDefaultMessage());

return result;

}

@ExceptionHandler(BindException.class)

public Map<String,Object> handleBindException(BindException ex) {

//校驗 除了 requestbody 註解方式的引數校驗 對應的 bindingresult 為 BeanPropertyBindingResult

FieldError fieldError = ex.getBindingResult().getFieldError();

log.info("必填校驗異常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());

Map<String,Object> result = new HashMap<String,Object>();

result.put("respCode", "01002");

result.put("respMsg", fieldError.getDefaultMessage());

return result;

}

啟動後,提示就友好了

所以統一異常還是很有必要的。

總結

本章節主要是闡述了統一異常處理和資料的合法性校驗,同時簡單實現了一個自定義的註解類,大家在碰見已有註解無法解決時,可通過自定義的形式進行,當然對於通用而已,利用@Pattern(正則表示式)基本上都是可以實現的。

最後

目前網際網路上很多大佬都有springboot系列教程,如有雷同,請多多包涵了。本文是作者在電腦前一字一句敲的,每一步都是實踐的。若文中有所錯誤之處,還望提出,謝謝。

相關推薦

SpringBoot | 統一異常資料處理

前言 在web應用中,請求處理時,出現異常是非常常見的。所以當應用出現各類異常時,進行異常的捕獲或者二次處理(比如sql異常正常是不能外拋)是非常必要的,比如在開發對外api服務時,約定了響應的引數格式,如respCode、respMsg,呼叫方根據錯誤碼進行自己的業務

springboot學習:統一異常資料處理

springboot中,預設在傳送異常時,會跳轉值/error請求進行錯誤的展現,根據不同的Content-Type展現不同的錯誤結果,如json請求時,直接返回json格式引數。   瀏覽器訪問異常時:   使用postman訪問時:  

SpringBoot | 搭建Spring Boot Admin分散式微服務監控中心

(一)、什麼是Spring Boot Admin       Spring Boot Admin 是一個針對spring-boot的actuator介面進行UI美化封裝的監控工具。他可以返回在列表中瀏覽所有被監控spring-boot專案的基本資訊比如

SpringBoot | Spring boot 資料來源未配置,啟動異常

1、問題 在使Springboot自動生成的專案框架時如果選擇了資料來源,比如選擇了mysql依賴,生成專案之後,在沒有任何的配置時啟動會報一下異常,執行程式後,控制檯輸出錯誤日誌: 2018-12-04 14:00:46.890  WARN 6592 --- [ &n

SpringBoot | 過濾器監聽器攔截器

前言 在實際開發過程中,經常會碰見一些比如系統啟動初始化資訊、統計線上人數、線上使用者數、過濾敏高詞彙、訪問許可權控制(URL級別)等業務需求。這些對於業務來說一般上是無關的,業務方是無需關係的,業務只需要關係自己內部業務的事情。所以一般上實現以上的功能,都會或多或少的

“全棧2019”Java異常throw關鍵字詳解

技術 環境 零基礎 學習小組 轉載 基礎 tps dea 語言 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java異常第八章:throw關鍵字

使用攔截器記錄你的SpringBoot的請求日誌

請求日誌幾乎是所有大型企業級專案的必要的模組,請求日誌對於我們來說後期在專案執行上線一段時間用於排除異常、請求分流處理、限制流量等。請求日誌一般都會記錄請求引數、請求地址、請求狀態(Status Code)、SessionId、請求方法方式(Method)、請求時間、客戶端I

BOM

hci forward doc pri window arc ear query define /* * bom: * 提供了瀏覽器接口; * w3c為了把javascript最基本的只是標準化已經將bom的主要方面納入html5規範

《JavaScript 高級程序設計》變量作用域和內存問題

綁定 pro 擔心 block lock 效率 TE 二次 problem 目錄 變量的引用 執行環境及作用域 作用域鏈延長 塊級作用域 垃圾回收機制 變量的引用 當一個變量保存了基本數據類型時,此時對於變量的操作(賦值,運算)就是操作這個基本數據的本身,就算是賦值操

C++ Primer(五版) IO庫

cor 三種 cer record info pre hide std 2-0 練習8.1:考察如何管理流的狀態 1 istream& func(istream& is) 2 { 3 string buf; 4 while ( is>

《Java8實戰》-筆記(重構測試和除錯)

重構、測試和除錯 通過本書的前七章,我們瞭解了Lambda和Stream API的強大威力。你可能主要在新專案的程式碼中使用這些特性。如果你建立的是全新的Java專案,這是極好的時機,你可以輕裝上陣,迅速地將新特性應用到專案中。然而不幸的是,大多數情況下你沒有機會從頭開始一個全新的專案。很多時候,你不得不面

JavaScript介紹

1.不應該做什麼      有兩項原則要牢記於心:      漸進增強:我們總是從核心的部分,也就是從內容開始。應該根據內容使用標記實現良好的結構,然後再逐步地加強各種行為。這些增強行為既可以是CSS改進呈現效果,也可以是通過

_040_Android_Android面試寶典總結 --對話方塊資訊提示和選單

 轉自https://blog.csdn.net/wenzhi20102321/article/details/60868763,感謝作者的無私分享。 Android面試寶典總結         &nb

(雲吶運維手冊)服務商版註冊指南

1.管理員WEB官網註冊 如您的客戶端為PC,可使用WEB官網註冊方式。 通過WEB瀏覽器訪問www.easyitom.com,在產品官網的右上角點選“註冊”按鈕。     輸入註冊所需資訊,填入手機號碼,接收並輸入驗證碼,完成註冊。 註冊時需選擇貴企業的“企業型別”

阿里雲linux伺服器------域名購買備案和解析

當我們購買了伺服器後我們可以通過伺服器的公網ip直接訪問它,但在網際網路上你訪問別人的網站一般都是通過這個網站的網址(域名)來訪問它,而不是通過這個網站所在伺服器的ip地址。這樣做的好處:一是更加安全,不會暴露你伺服器的地址、二會起到一定的宣傳作用,因為我們花錢買個域名總會希望它有點特殊的含義。

“全棧2019”JavaIntelliJ IDEA設定註釋不顯示在行首

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第八章:IntelliJ IDEA設定註釋不顯示在行首 下一章 “全棧2019”Java第九

SpringBoot | 搭建SpringBoot整合Mybatis+druid連線池監控

前言     今年註定是不平凡的一年,Spring家族的一套SpringBoot+SpringCloud掀起來一陣熱潮,網際網路微服務技術火熱膨脹,身邊的程式設計師都在學習,我經不住誘惑,準備從零開始學,並且以記錄部落格的形式; 一. 建立springboot工程

SpringBoot | 搭建SpringBoot監控中心Actuator

(一)、什麼是Actuator        Actuator是spring boot的一個附加功能,可幫助你在應用程式生產環境時監視和管理應用程式。可以使用HTTP的各種請求來監管,審計,收集應用的執行情況。Spring Boot Actuator提

SpringBoot | springboot 專案啟動讀取按照順序讀取配置檔案

1.按照順序讀取配置檔案工具類 import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; im

深入理解Nginxnginx基礎架構

Nginx效能指標 吞吐量(頻寬指的是單個連線的吞吐量) 單次請求的延遲性 網路效率:長連線減少建立、關閉連線的成本,壓縮演算法增加資訊攜帶量 Nginx  模組設計 NGX_CONF_MODULE:唯一隻有一個模組(ngx_conf_modul