<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      背景

      在開(kāi)發(fā)中經(jīng)常需要寫一些字段校驗(yàn)的代碼,比如非空,長(zhǎng)度限制,郵箱格式驗(yàn)證等等,導(dǎo)致充滿了if-else 的代碼,不僅相當(dāng)冗長(zhǎng),而且很讓人抓狂。

      hibernate validator(官方文檔 <http://hibernate.org/validator/documentation/>
      )提供了一套比較完善、便捷的驗(yàn)證實(shí)現(xiàn)方式。它定義了很多常用的校驗(yàn)注解,我們可以直接將這些注解加在我們JavaBean
      的屬性上面,就可以在需要校驗(yàn)的時(shí)候進(jìn)行校驗(yàn)了。在Spring Boot 火熱的現(xiàn)在,該工具已經(jīng)包含在spring-boot-starter-web
      中,不需額外引入其他包。

      一、快速入門

      1.1 在UserDTO中聲明要檢查的參數(shù)

      校驗(yàn)說(shuō)明見(jiàn)代碼中注釋
      @Data public class UserDTO { /** 性別(不校驗(yàn)) */ private String sex; /**
      用戶名(校驗(yàn):不能為空,不能超過(guò)20個(gè)字符串) */ @NotBlank(message = "用戶名不能為空") @Length(max = 20,
      message = "用戶名不能超過(guò)20個(gè)字符") private String userName; /** * 手機(jī)號(hào)(校驗(yàn):不能為空且按照正則校驗(yàn)格式)
      */ @NotBlank(message = "手機(jī)號(hào)不能為空") @Pattern(regexp =
      "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機(jī)號(hào)格式有誤") private String mobile; /**
      郵箱(校驗(yàn):不能唯恐且校驗(yàn)郵箱格式) */ @NotBlank(message = "聯(lián)系郵箱不能為空") @Email(message =
      "郵箱格式不對(duì)") private String email; }
      1.2 接口處聲明要檢查的參數(shù)

      需要在Controller層的入?yún)⑽恢糜聾Validated 注解聲明
      @RestController @RequestMapping("/demo") public class ValidatorDemoController
      { /** * 注解參數(shù)校驗(yàn)案例 * @param userDTO * @return */ @PostMapping("/test") public
      HttpResult test(@Validated UserDTO userDTO) { return
      HttpResult.success(userDTO); } }
      這里的HttpResult 是Van 自己封裝的一個(gè)結(jié)果集,詳見(jiàn)文末Github地址的源碼。

      1.3 Web全局異常捕獲

      @Valid 在 Spring Boot中進(jìn)行綁定參數(shù)校驗(yàn)時(shí)會(huì)拋出異常,需要在Spring Boot中處理。
      @RestControllerAdvice @Slf4j public class WebExceptionHandler { /** * 方法參數(shù)校驗(yàn) *
      @param e * @return */ @ExceptionHandler(BindException.class) public HttpResult
      handleMethodArgumentNotValidException(BindException e) {
      log.error(e.getMessage(), e); return
      HttpResult.failure(400,e.getBindingResult().getFieldError().getDefaultMessage());
      } @ExceptionHandler(Exception.class) public HttpResult
      handleException(Exception e) { log.error(e.getMessage(), e); return
      HttpResult.failure(400, "系統(tǒng)繁忙,請(qǐng)稍后再試"); } }
      1.4 測(cè)試

      測(cè)試工具采用的postman

      * 請(qǐng)求方式:POST
      * 請(qǐng)求地址:localhost:8080/demo/test
      * 請(qǐng)求參數(shù): userName:Van mobile:17098705205 email:123
      * 返回結(jié)果: { "success": false, "code": 400, "data": null, "message": "郵箱格式不對(duì)" }
      * 說(shuō)明
      * 更多注解,請(qǐng)各位自行嘗試;
      * 測(cè)試結(jié)果證明:參數(shù)校驗(yàn)生效,且按照我們?cè)O(shè)定的結(jié)果集返回異常信息。
      1.5 常見(jiàn)的校驗(yàn)注解

      * @Null:被注釋的元素必須為 null

      * @NotNull:被注釋的元素必須不為 null

      * @AssertTrue:被注釋的元素必須為 true

      * @AssertFalse:被注釋的元素必須為 false

      * @Min(value):被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
      * @Max(value):被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
      * @DecimalMin(value):被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
      * @DecimalMax(value):被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
      * @Size(max=, min=):被注釋的元素的大小必須在指定的范圍內(nèi)

      * @Digits (integer, fraction):被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi)
      * @Past:被注釋的元素必須是一個(gè)過(guò)去的日期
      @Future:被注釋的元素必須是一個(gè)將來(lái)的日期

      * @Pattern(regex=,flag=):被注釋的元素必須符合指定的正則表達(dá)式
      * @NotBlank(message =):驗(yàn)證字符串非null,且長(zhǎng)度必須大于0

      * @Email:被注釋的元素必須是電子郵箱地址

      * @Length(min=,max=):被注釋的字符串的大小必須在指定的范圍內(nèi)

      * @NotEmpty:被注釋的字符串的必須非空

      * @Range(min=,max=,message=):被注釋的元素必須在合適的范圍內(nèi)
      二、自定義注解校驗(yàn)

      hibernate validator
      自帶的注解可以搞定簡(jiǎn)單的參數(shù)校驗(yàn),加上正則的寫法,能解決絕大多數(shù)參數(shù)校驗(yàn)情況。但是,有些情況,比如:校驗(yàn)是否登錄,就需要我們自定義注解校驗(yàn)了。為了方便測(cè)試,我這里以身份證校驗(yàn)為例完成自定義校驗(yàn)的過(guò)程。

      2.1 身份證校驗(yàn)工具類
      public class IdCardValidatorUtils { protected String codeAndCity[][] = {{"11",
      "北京"}, {"12", "天津"}, {"13", "河北"}, {"14", "山西"}, {"15", "內(nèi)蒙古"}, {"21", "遼寧"},
      {"22", "吉林"}, {"23", "黑龍江"}, {"31", "上海"}, {"32", "江蘇"}, {"33", "浙江"}, {"34",
      "安徽"}, {"35", "福建"}, {"36", "江西"}, {"37", "山東"}, {"41", "河南"}, {"42", "湖北"},
      {"43", "湖南"}, {"44", "廣東"}, {"45", "廣西"}, {"46", "海南"}, {"50", "重慶"}, {"51",
      "四川"}, {"52", "貴州"}, {"53", "云南"}, {"54", "西藏"}, {"61", "陜西"}, {"62", "甘肅"},
      {"63", "青海"}, {"64", "寧夏"}, {"65", "新疆"}, {"71", "臺(tái)灣"}, {"81", "香港"}, {"82",
      "澳門"}, {"91", "國(guó)外"}}; private String cityCode[] = {"11", "12", "13", "14",
      "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42",
      "43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64",
      "65", "71", "81", "82", "91"}; // 每位加權(quán)因子 private static int power[] = {7, 9,
      10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; // 第18位校檢碼 private String
      verifyCode[] = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}; /** *
      驗(yàn)證所有的身份證的合法性 * * @param idcard * @return */ public static boolean
      isValidatedAllIdcard(String idcard) { if (idcard.length() == 15) { idcard =
      convertIdcarBy15bit(idcard); } return isValidate18Idcard(idcard); } /** *
      將15位的身份證轉(zhuǎn)成18位身份證 * * @param idcard * @return */ public static String
      convertIdcarBy15bit(String idcard) { String idcard17 = null; // 非15位身份證 if
      (idcard.length() != 15) { return null; } if (isDigital(idcard)) { // 獲取出生年月日
      String birthday = idcard.substring(6, 12); Date birthdate = null; try {
      birthdate = new SimpleDateFormat("yyMMdd").parse(birthday); } catch
      (ParseException e) { e.printStackTrace(); } Calendar cday =
      Calendar.getInstance(); cday.setTime(birthdate); String year =
      String.valueOf(cday.get(Calendar.YEAR)); idcard17 = idcard.substring(0, 6) +
      year + idcard.substring(8); char c[] = idcard17.toCharArray(); String checkCode
      = ""; if (null != c) { int bit[] = new int[idcard17.length()]; // 將字符數(shù)組轉(zhuǎn)為整型數(shù)組
      bit = converCharToInt(c); int sum17 = 0; sum17 = getPowerSum(bit); //
      獲取和值與11取模得到余數(shù)進(jìn)行校驗(yàn)碼 checkCode = getCheckCodeBySum(sum17); // 獲取不到校驗(yàn)位 if (null ==
      checkCode) { return null; } // 將前17位與第18位校驗(yàn)碼拼接 idcard17 += checkCode; } } else
      { // 身份證包含數(shù)字 return null; } return idcard17; } /** * @param idCard * @return */
      public static boolean isValidate18Idcard(String idCard) { // 非18位為假 if
      (idCard.length() != 18) { return false; } // 獲取前17位 String idcard17 =
      idCard.substring(0, 17); // 獲取第18位 String idcard18Code = idCard.substring(17,
      18); char c[] = null; String checkCode = ""; // 是否都為數(shù)字 if (isDigital(idcard17))
      { c = idcard17.toCharArray(); } else { return false; } if (null != c) { int
      bit[] = new int[idcard17.length()]; bit = converCharToInt(c); int sum17 = 0;
      sum17 = getPowerSum(bit); // 將和值與11取模得到余數(shù)進(jìn)行校驗(yàn)碼判斷 checkCode =
      getCheckCodeBySum(sum17); if (null == checkCode) { return false; } //
      將身份證的第18位與算出來(lái)的校碼進(jìn)行匹配,不相等就為假 if (!idcard18Code.equalsIgnoreCase(checkCode)) {
      return false; } } return true; } /** * 18位身份證號(hào)碼的基本數(shù)字和位數(shù)驗(yàn)校 * * @param idCard *
      @return */ public boolean is18Idcard(String idCard) { return
      Pattern.matches("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([\\d|x|X]{1})$",
      idCard); } /** * 數(shù)字驗(yàn)證 * * @param str * @return */ public static boolean
      isDigital(String str) { return str == null || "".equals(str) ? false :
      str.matches("^[0-9]*$"); } /** * 將身份證的每位和對(duì)應(yīng)位的加權(quán)因子相乘之后,再得到和值 * * @param bit *
      @return */ public static int getPowerSum(int[] bit) { int sum = 0; if
      (power.length != bit.length) { return sum; } for (int i = 0; i < bit.length;
      i++) { for (int j = 0; j < power.length; j++) { if (i == j) { sum = sum +
      bit[i] * power[j]; } } } return sum; } /** * 將和值與11取模得到余數(shù)進(jìn)行校驗(yàn)碼判斷 * * @param
      sum17 * @return 校驗(yàn)位 */ public static String getCheckCodeBySum(int sum17) {
      String checkCode = null; switch (sum17 % 11) { case 10: checkCode = "2"; break;
      case 9: checkCode = "3"; break; case 8: checkCode = "4"; break; case 7:
      checkCode = "5"; break; case 6: checkCode = "6"; break; case 5: checkCode =
      "7"; break; case 4: checkCode = "8"; break; case 3: checkCode = "9"; break;
      case 2: checkCode = "x"; break; case 1: checkCode = "0"; break; case 0:
      checkCode = "1"; break; } return checkCode; } /** * 將字符數(shù)組轉(zhuǎn)為整型數(shù)組 * * @param c *
      @return * @throws NumberFormatException */ public static int[]
      converCharToInt(char[] c) throws NumberFormatException { int[] a = new
      int[c.length]; int k = 0; for (char temp : c) { a[k++] =
      Integer.parseInt(String.valueOf(temp)); } return a; } public static void
      main(String[] args) { String idCardForFalse = "350583199108290106"; String
      idCardForTrue = "350583197106150219"; if
      (IdCardValidatorUtils.isValidatedAllIdcard(idCardForTrue)) {
      System.out.println("身份證校驗(yàn)正確"); } else { System.out.println("身份證校驗(yàn)錯(cuò)誤!"); } } }
      2.2 自定義注解
      @Documented @Target({ElementType.PARAMETER, ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy =
      IdentityCardNumberValidator.class) public @interface IdentityCardNumber {
      String message() default "身份證號(hào)碼格式不正確"; Class<?>[] groups() default {}; Class<?
      extends Payload>[] payload() default {}; }
      仔細(xì)的你會(huì)發(fā)現(xiàn),相對(duì)于一般的自定義注解,該注解:
      @Constraint(validatedBy = IdentityCardNumberValidator.class)
      ,該注解的作用就是調(diào)用身份證校驗(yàn)的工具。

      2.3 在UserDTO 需要校驗(yàn)的字段添加聲明
      /** * 身份證號(hào)(校驗(yàn):自定義注解校驗(yàn)) */ @IdentityCardNumber private String idNumber;
      2.4 控制層接口
      @RestController @RequestMapping("/custom") public class
      ValidatorCustomController { /** * 自定義注解參數(shù)校驗(yàn)案例 * @param userDTO * @return */
      @PostMapping("/test") public HttpResult test(@Validated UserDTO userDTO) {
      return HttpResult.success(userDTO); } }
      2.5 自定義注解的測(cè)試

      * 請(qǐng)求方式:POST
      * 請(qǐng)求地址:localhost:8080/private/test
      * 請(qǐng)求參數(shù): userName:Van mobile:17098705205 email:[email protected]
      idNumber:350583199108290106
      * 返回結(jié)果: { "success": false, "code": 400, "data": null, "message":
      "身份證號(hào)碼格式不正確" }
      三、分組校驗(yàn)

      除了上述的校驗(yàn)外,可能還有這種需求:

      在創(chuàng)建用戶信息時(shí),不需要校驗(yàn)userId;但在更新用戶信息時(shí),需要校驗(yàn)userId,而用戶名,郵箱等兩種情況都得校驗(yàn)。這種情況,就可以分組校驗(yàn)來(lái)解決了。

      3.1 定義分組接口

      * Create.java import javax.validation.groups.Default; public interface Create
      extends Default { }
      * Update.java import javax.validation.groups.Default; public interface Update
      extends Default { }
      3.2 在UserDTO 需要校驗(yàn)的字段添加聲明
      /** * 用戶id(只有在有Update分組中校驗(yàn)非空) */ @NotNull(message = "id 不能為空", groups =
      Update.class) private Long userId;
      3.3 控制層入?yún)⑽恢眠M(jìn)行聲明
      @RestController @RequestMapping("/groups") public class
      ValidatorGroupsController { /** * 更新數(shù)據(jù),需要傳入userID * @param userDTO * @return */
      @PostMapping("/update") public HttpResult
      updateData(@Validated(Update.class)UserDTO userDTO) { return
      HttpResult.success(userDTO); } /** * 新增數(shù)據(jù),不需要傳入userID * @param userDTO *
      @return */ @PostMapping("/create") public HttpResult
      createData(@Validated(Create.class)UserDTO userDTO) { return
      HttpResult.success(userDTO); } }
      3.4 分組校驗(yàn)的測(cè)試-新增測(cè)試

      * 請(qǐng)求方式:POST
      * 請(qǐng)求地址:localhost:8080/groups/create
      * 請(qǐng)求參數(shù): userName:Van mobile:17098705205 email:[email protected]
      idNumber:350583197106150219 userId:
      * 返回結(jié)果: { "success": true, "code": 200, "data": { "userId": null, "sex":
      null, "userName": "Van", "mobile": "17098705205", "email": "[email protected]",
      "idNumber": "350583197106150219", "passWord": null }, "message": null }
      請(qǐng)求成功,說(shuō)明新增請(qǐng)求,不檢驗(yàn)userId,即userId可以為空。

      3.5 分組校驗(yàn)的測(cè)試-更新測(cè)試

      * 請(qǐng)求方式:POST
      * 請(qǐng)求地址:localhost:8080/groups/update
      * 請(qǐng)求參數(shù):同上(3.4)
      * 返回結(jié)果: { "success": false, "code": 400, "data": null, "message": "id 不能為空" }
      請(qǐng)求失敗,說(shuō)明更新請(qǐng)求,檢驗(yàn)userId,即userId不能為空。

      結(jié)合 3.4 與 3.5 的測(cè)試結(jié)果,說(shuō)明分組校驗(yàn)成功。

      四、總結(jié)

      希望大家寫的每一行代碼都是業(yè)務(wù)需要,而不是無(wú)聊且無(wú)窮無(wú)盡的參數(shù)校驗(yàn)。

      Github 示例代碼
      <https://github.com/vanDusty/SpringBoot-Home/tree/master/springboot-demo-parameter/validator-demo>

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問(wèn)題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          做 视频免费观看网站 | 极品无码| 色婷婷久久久亚洲一区二区三区 | 欧美在线视频网站 | 国产精品免费久久久久久久久 | 很污很黄的网站 | 欧美三级一区 | 96精品久久久久久久久久 | 国产一级婬片A片狂添 | 91视频专区 |