在后端的接口開發過程,實際上每一個接口都或多或少有不同規則的參數校驗,有一些是基礎校驗,如非空校驗、長度校驗、大小校驗、格式校驗;也有一些校驗是業務校驗,如學號不能重重復、手機號不能重復注冊等;對于業務校驗,是需要和數據庫交互才能知道校驗結果;對于參數的基礎校驗,是有一些共有特征可以抽象出來,可以做成一個通用模板(java就是一種面向對象的編程語言,還記得天天快要說爛問爛的面向對象的三大特性嗎?)。基于實際場景的需要,java API中定義了一些Bean校驗的規范標準(JSR303:validation-api),但是沒有具體實現,不過hibernate validation和spring validation都提供了一些比較優秀的實現。如果在項目里,你還是像類似這樣的方式來進行參數校驗就太low了,活該加班到天亮(當然如果你所在公司目前仍然用統計代碼量來考核你的工作,就算我沒說,你可以繼續使用這種方式)。
@PostMapping("/add")public String add(Student student) { if (null == student) { throw new RuntimeException("學生不為空"); } if ("".equals(student.getStuCode())) { throw new RuntimeException("學號不能為空"); } if ("".equals(student.getStuName())) { throw new RuntimeException("學生姓名不能為空"); } if (null == student.getTeacher()) { throw new RuntimeException("學生的老師的不能為空"); } if ("".equals(student.getTeacher().getTecName())) { throw new RuntimeException("學生的老師的姓名不能為空"); } if ("".equals(student.getTeacher().getSubject())) { throw new RuntimeException("學生的老師的所授科目不為能空"); } return "success";}
分享的這篇文章里的校驗參數注解使用方法,我是在一個springboot項目里親自重新測試驗證過的,springboot的版本是2.3.9.RELEASE,另外也引入了關于參數校驗的starter包,這樣就不用額外去引關于參數校驗的其他包了;
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.3.9.RELEASE</version></dependency>
在java項目中,前端請求后端的接口中,常用的請求類型主要是post和get。
@Valid | 被注釋的元素是一個對象,需要檢查此對象的所有字段值 |
@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
注解 | 作用 |
被注釋的元素必須是電子郵箱地址 | |
@Length(min=, max=) | 被注釋的字符串的大小必須在指定的范圍內 |
@NotEmpty | 被注釋的字符串的必須非空 |
@Range(min=, max=) | 被注釋的元素必須在合適的范圍內 |
@NotBlank | 被注釋的字符串的必須非空 |
@URL(protocol=, host=, port=, regexp=, flags=) | 被注釋的字符串必須是一個有效的url |
@CreditCardNumber | 被注釋的字符串必須通過Luhn校驗算法, 銀行卡,信用卡等號碼一般都用Luhn 計算合法性 |
@ScriptAssert (lang=, script=, alias=) | 要有Java Scripting API 即JSR 223 ("Scripting for the JavaTM Platform")的實現 |
@SafeHtml (whitelistType=, additionalTags=) | classpath中要有jsoup包 |
參數的基礎校驗,通常是指的非空、長度、最大值、最小值、格式(數字、郵箱、正則)等這些場景的校驗。
1.在controller層的方法的形參數前面加一個@Valid或@Validated的注解;
2.在用@RequestBody修飾的類的屬性上加上約束注解,如@NotNull、@Length、@NotBlank;
3.@RequestBody參數在觸發校驗規則時,會拋出MethodArgumentNotValidException,這里使用統一的異常處理機制來處理異常;
總結:第1步的valid的作用就是一個標記,標明這個參數需要進行校驗;第2步的約束注解的上注明校驗的規則;第3步的統一校驗機制是前后臺請求后臺接口時,如果校驗參數的校驗規則后會拋出異常,異常附帶有約束注解上的提示信息,那么通過異常統一處理機制就可以統一處理異常信息,并以合適的方式返回給前臺(所謂合適的方式是指異常信息的格式可以自行制定)。
@PostMapping("/add")public Student add( @Valid@RequestBody Student student){ System.out.println(student.getStuName()); return student;}@Datapublic class Student { @NotNull(message = "學號不能為空") @Length(min = 2, max = 4, message = "學號的長度范圍是(2,4)") private String stuCode; @NotNull(message = "姓名不能為空") @Length(min = 2, max = 3, message = "姓名的長度范圍是(2,3)") private String stuName;}
@RequestParam參數/@PathVariable參數
1.在controller層的控制類上添加@Validated注解;
2.在controller層方法的校驗參數上添加約束注解,如@NotNull、@Pattern;
3.@RequestParam參數/@PathVariable參數在觸發校驗規則時,會拋出ConstraintViolationException類型的異常,所以在統一異常處理機制中添加對這種類型異常的處理機制;
@RestController@RequestMapping("/student")@Validatedpublic class StudentController { @GetMapping("/{sex}/info") public String getBySex(@PathVariable("sex") @Pattern(regexp = "boy||girl",message = "學生性別只能是boy或girl") String sex) { System.out.println("學生性別:" + sex); return "success"; } @GetMapping("/getOne") public String getOne(@NotNull(message = "學生姓名不能為空") String stuName, @NotNull(message = "學生學號不能為空") String stuCode) { System.out.println("stuName:" + stuName + ",stuCode:" + stuCode); return "success"; }}
@RestControllerAdvicepublic class CommonExceptionHandler { /** * 用于捕獲@RequestBody類型參數觸發校驗規則拋出的異常 * * @param e * @return */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public String handleValidException(MethodArgumentNotValidException e) { StringBuilder sb = new StringBuilder(); List<ObjectError> allErrors = e.getBindingResult().getAllErrors(); if (!CollectionUtils.isEmpty(allErrors)) { for (ObjectError error : allErrors) { sb.append(error.getDefaultMessage()).append(";"); } } return sb.toString(); } /** * 用于捕獲@RequestParam/@PathVariable參數觸發校驗規則拋出的異常 * * @param e * @return */ @ExceptionHandler(value = ConstraintViolationException.class) public String handleConstraintViolationException(ConstraintViolationException e) { StringBuilder sb = new StringBuilder(); Set<ConstraintViolation<?>> conSet = e.getConstraintViolations(); for (ConstraintViolation<?> con : conSet) { String message = con.getMessage(); sb.append(message).append(";"); } return sb.toString(); }}
在實際項目中有這樣一種場景,用來接收參數的類的屬性字段也是一個對象,屬性對象的字段也需要進行必要的參數校驗,這個時候可以使用嵌套校驗來解決這個問題,hibernate-validator提供了具體的解決方式。
1.在controller層方法的形參數前添加@Validated注解,如果有分組校驗的場景,則注明分組信息;如果校驗不需要分組,可以不注明分組信息;
2.在接收參數的類的屬性是對象的字段上添加@Valide注解,這里需要注意的是一定是@Valid,不是@Validated,因為@Valid的實現是由hibernate-validator提供,有嵌套校驗的能力,而@Validated是由spring-validation提供的具體實現方式,@Validated有分組校驗的能力,但是沒有嵌套校驗的能力;(java API規范(JSR303)定義了Bean的校驗標準validation-api,但是沒有具體的實現,所以各有各的實現,在功能上也是有區別的)
3.嵌套屬性類上的約束注解的用法,與用來接收參數的對象屬性上的約束注解的用法是一樣的;
總結:@Valid的實現是由hibernate-validator提供,有嵌套校驗的能力,但是沒有分組校驗的能力,@Validated是由spring-validation提供的具體實現方式,@Validated有分組校驗的能力,但是沒有嵌套校驗的能力,在使用的過程須特別注意,要根據實際需要進行剪裁。
@PostMapping("/addStuaAndTeach")public String addStuaAndTeach(@Validated(AddStuAndTeach.class) @RequestBody Student student){ System.out.println("學生的工號:"+student.getStuCode()+",學生的老師的姓名:"+student.getTeacher().getTecName()); return "success";}
@Datapublic class Teacher { @NotNull(message = "學生的老師姓名不能為空",groups = AddStuAndTeach.class) private String tecName; @NotNull(message = "學生的老師教授科目不能為空",groups = AddStuAndTeach.class) private String subject;}public interface AddStuAndTeach {}
@Datapublic class Student { @NotNull(message = "學生id不能為空",groups = QueryDetail.class) private Integer id; @NotNull(message = "學號不能為空",groups = AddStudent.class) @Length(min = 2, max = 4, message = "學號的長度范圍是(2,4)") private String stuCode; @NotNull(message = "姓名不能為空",groups = AddStudent.class) @Length(min = 2, max = 3, message = "姓名的長度范圍是(2,3)",groups = AddStudent.class) private String stuName; @Valid @NotNull(message = "學生的老師不能為空",groups = AddStuAndTeach.class) private Teacher teacher;}
在實際的項目中,可能多個方法使用同一個類來接收參數,但是不同的方法的校驗規則又是不同的,這個時候就可以使用分組校驗的方式來解決這個問題了,spring-validation提供了具體的實現方式。
1.聲明分組用的接口,比如添加和查詢詳情的時候,校驗的規則肯定是不一樣的,添加的時候一般不用傳id,由后臺自增長生成,查詢詳情的時候id是必須傳的;
2.在controller層方法的校驗參數上添加@Validated參數,同時注解里要注明校驗參數的分組信息;
3.在校驗參數的類上的線束注解上,也要注明校驗參數的分組信息;
總結:在接口的入口方法參數上、校驗參數上都注明了分組的信息,那么接口被用的時候,就可以根據不同的分組信息執行不同約束注解的校驗邏輯了,這個能力是spring-validation提供的,所以這種場景下,controller層方法的上注解要用@Validated,@Valid注解沒有這種能力。
//用于添加場景參數校驗分組public interface AddStudent {}
//用于查詢詳情場景參數校驗分組public interface QueryDetail {}
@PostMapping("/add")public Student add(@Validated(AddStudent.class) @RequestBody Student student) { System.out.println(student.getStuName()); return student;}@PostMapping("/detail")public String detail(@Validated(QueryDetail.class)@RequestBody Student student){ System.out.println("學生id:"+student.getId()); return "success";}
@Datapublic class Student { @NotNull(message = "學生id不能為空",groups = QueryDetail.class) private Integer id; @NotNull(message = "學號不能為空",groups = AddStudent.class) @Length(min = 2, max = 4, message = "學號的長度范圍是(2,4)") private String stuCode; @NotNull(message = "姓名不能為空",groups = AddStudent.class) @Length(min = 2, max = 3, message = "姓名的長度范圍是(2,3)",groups = AddStudent.class) private String stuName; }
本文鏈接:http://www.www897cc.com/showinfo-26-35335-0.html優雅的springboot參數校驗,你學會了嗎?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Solid 作者從 React 中學到最重要的是什么?
下一篇: 我們一起聊聊Python協程和異步編程