利用Spring Validation實現(xiàn)輸入驗證功能
概述
校驗例子
大家平時編碼中經(jīng)常涉及參數(shù)的校驗,對于一個用戶注冊的方法來說會校驗用戶名密碼信息:
public class UserController { public ResponseEntity<String> registerUser(String username, String password) { if (username == null || username.isEmpty()) { return ResponseEntity.badRequest().body("用戶名不能為空"); } if (password == null || password.isEmpty()) { return ResponseEntity.badRequest().body("密碼不能為空"); } if (password.length() < 6) { return ResponseEntity.badRequest().body("密碼長度至少為6位"); } // 處理用戶注冊邏輯 return ResponseEntity.ok("用戶注冊成功"); } }
上述例子中需要手動編寫參數(shù)校驗邏輯的過程。雖然對于這個簡單的示例而言,手動編寫校驗邏輯可能是可行的,但是對于復雜的驗證規(guī)則和多個參數(shù)的情況,手動編寫校驗邏輯會變得冗長、難以維護和復用。
引入現(xiàn)代的校驗框架如Spring Validation
可以幫助解決這些問題,提供更高效、統(tǒng)一和可維護的參數(shù)校驗方案。
Bean Validation規(guī)范
- JSR303/JSR-349/JSR-380: JSR303(
Bean Validation
)是一項標準,只提供規(guī)范不提供實現(xiàn),規(guī)定一些校驗規(guī)范即校驗注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349(Bean Validation 1.1
)是其的升級版本,添加了一些新特性。JSR-380(Bean Validation 2.0
,JSR380標準 )對其規(guī)范進一步擴展和增強。 hibernate validation
:hibernate validation
是對這個規(guī)范的實現(xiàn),并增加了一些其他校驗注解,如@Email,@Length,@Range等等Spring validation
:spring validation
對hibernate validation
進行了二次封裝,在springmvc模塊中添加了自動校驗,并將校驗信息封裝進了特定的類中
Bean Validation的主頁:Jakarta Bean Validation - Home
Bean Validation的參考實現(xiàn):GitHub - hibernate/hibernate-validator: Hibernate Validator - Jakarta Bean Validation Reference Implementation
相關版本兼容性
Bean Validation | Hibernate Validation | JDK | Spring Boot |
---|---|---|---|
1.1 | 5.4 + | 6+ | 1.5.x |
2.0 | 6.0 + | 8+ | 2.0.x |
3.0 | 7.0 + | 9+ | 2.0.x |
3.0后Bean Validation
改名為Jakarta Bean Validation 3.0
了。 如果你的項目版本是jdk1.8的,不要使用hibernate-validator 7.0
的版本,它里面的依賴的jakarta.validation-api:3.0
是需要jdk1.9的部分支持的。
Spring Validation注解
Spring Validation建立在Java Bean Validation(JSR 380)的基礎上,為開發(fā)人員提供了一組注解和工具,用于定義和執(zhí)行數(shù)據(jù)驗證規(guī)則。它允許開發(fā)人員在應用程序中定義驗證規(guī)則,并使用這些規(guī)則來驗證輸入數(shù)據(jù)、請求參數(shù)、領域對象等。
@Validated:可以用在類型、方法和方法參數(shù)上。但是不能用在成員屬性(字段)上
@Valid:可以用在方法、構造函數(shù)、方法參數(shù)和成員屬性(字段)上
常用注解標簽如下:
標簽 | 說明 |
---|---|
@Null | 限制只能為null |
@NotNull | 限制必須不為null |
@AssertFalse | 限制必須為false |
@AssertTrue | 限制必須為true |
@DecimalMax(value) | 限制必須為一個不大于指定值的數(shù)字 |
@DecimalMin(value) | 限制必須為一個不小于指定值的數(shù)字 |
@Digits(integer,fraction) | 限制必須為一個小數(shù),且整數(shù)部分的位數(shù)不能超過integer,小數(shù)部分的位數(shù)不能超過fraction |
@Future | 限制必須是一個將來的日期 |
@Max(value) | 限制必須為一個不大于指定值的數(shù)字 |
@Min(value) | 限制必須為一個不小于指定值的數(shù)字 |
@Past | 限制必須是一個過去的日期 |
@Pattern(value) | 限制必須符合指定的正則表達式 |
@Size(max,min) | 限制字符長度必須在min到max之間 |
@Past | 驗證注解的元素值(日期類型)比當前時間早 |
@NotEmpty | 驗證注解的元素值不為null且不為空(字符串長度不為0、集合大小不為0) |
@NotBlank | 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同于@NotEmpty,@NotBlank只應用于字符串且在比較時會去除字符串的空格 |
驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email格式 |
hibernate-validator 校驗Java Bean
- pom引入hibernate-validator
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.2.0.Final</version> </dependency>
- 創(chuàng)建一個Java Bean,我們校驗一下用戶名跟年齡
public class User { @NotBlank(message = "用戶名不能為空") private String username; @Min(value = 18, message = "年齡不能小于18歲") private int age; // 構造函數(shù)、Getter 和 Setter 方法 }
- 執(zhí)行校驗
public class ValidatorTest { public static void main(String[] args) { // 創(chuàng)建校驗器 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); // 創(chuàng)建用戶對象 User user = new User(); user.setUsername(""); user.setAge(16); // 執(zhí)行校驗 Set<ConstraintViolation<User>> violations = validator.validate(user); // 處理校驗結果 if (!violations.isEmpty()) { for (ConstraintViolation<User> violation : violations) { System.out.println(violation.getMessage()); } } else { System.out.println("校驗通過"); } } }
用Spring Validation提高生產(chǎn)力
Spring Validation引入
添加pom依賴
Spring Validation
校驗包被獨立成了一個starter
組件,引入如下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
如果是spring-boot-starter-web
不用引入了,spring-boot-starter-web
集成了spring-boot-starter-validation
,默認可以不加spring-boot-starter-validation
,它同時也集成了hibernate-validator
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
hibernate Validator校驗器
這里我們定義hibernate校驗器用于校驗參數(shù)
@Configuration @EnableAutoConfiguration public class HibernateValidatorConfiguration { @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); processor.setValidator(validator()); processor.setProxyTargetClass(true); return processor; } @Bean public Validator validator() { return Validation .byProvider(HibernateValidator.class) .configure() .addProperty("hibernate.validator.fail_fast", "true") .buildValidatorFactory() .getValidator(); } }
全局異常處理
每個Controller
方法中都如果都寫一遍對校驗結果信息的處理,使用起來還是很繁瑣??梢酝ㄟ^全局異常處理的方式統(tǒng)一處理校驗異常。
@ControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(value = ConstraintViolationException.class) @ResponseBody public ApiResult defaultInsuranceExceptionHandler(ConstraintViolationException e) { log.error("校驗錯誤:{}", e.getMessage()); return ApiResult.error("error", e.getMessage()); } @ExceptionHandler(value = MissingServletRequestParameterException.class) @ResponseBody public ApiResult handleMissingServletRequestParameter(MissingServletRequestParameterException ex) { String error = ex.getParameterName() + " 參數(shù)為空"; return ApiResult.error("error", error); } }
Controller接口Bean校驗
首先,假設我們有一個用戶注冊的請求對象 UserRegistrationRequest
,其中包含用戶名和密碼字段。
@Data public class UserRegistrationRequest { @NotBlank(message = "用戶名不能為空") private String username; @NotBlank(message = "密碼不能為空") @Size(min = 6, message = "密碼長度至少為6位") private String password; }
我們使用了兩個注解進行參數(shù)校驗:
@NotBlank
:該注解用于驗證字段不能為空或空格,并可以通過message
屬性指定驗證失敗時的錯誤消息。@Size
:該注解用于驗證字段的長度,我們指定了密碼的最小長度為6,并通過message
屬性定義了驗證失敗時的錯誤消息。
接下來我們定義一個Controller接口
@RestController public class UserController { @PostMapping("/register") public ResponseEntity<String> registerUser(@Valid @RequestBody UserRegistrationRequest request) { // 處理用戶注冊邏輯 return ResponseEntity.ok("用戶注冊成功"); } }
@RequestParam 參數(shù)校驗
首先需要將MethodValidationPostProcessor設置成cglib代理
processor.setProxyTargetClass(true);
controller實現(xiàn)
@RestController public class ValidController implements ValidClient { @Override public String queryById(Integer id) { return "id:" + id; } }
分組校驗
還是看上面那個Controller接口校驗的例子,如果我們注冊的時候需要校驗用戶名和密碼,重置密碼的時候只校驗密碼該怎么校驗呢?這個時候就用到了分組校驗了。
- 首先,我們需要定義一個新的分組,用于更新場景中的驗證,例如
UpdateGroup
。
public interface UpdateGroup { }
- 接下來,我們需要在
UserRegistrationRequest
類的username
字段上使用@Validated
注解,并通過groups
屬性指定要應用的驗證分組。
public class UserRegistrationRequest { @NotBlank(message = "用戶名不能為空", groups = {RegistrationGroup.class}) private String username; @NotBlank(message = "密碼不能為空") @Size(min = 6, message = "密碼長度至少為6位") private String password; // 其他字段和方法 }
在上述示例中,我們將@Validated
注解應用到username
字段,并通過groups
屬性指定了RegistrationGroup.class
,這意味著在注冊場景中會進行校驗。
- 我們可以使用
@Validated
注解來指定不要應用任何驗證分組。
@RestController public class UserController { @PostMapping("/register") public ResponseEntity<String> registerUser(@Validated(RegistrationGroup.class) @RequestBody UserRegistrationRequest request) { // 處理用戶注冊邏輯 return ResponseEntity.ok("用戶注冊成功"); } @PostMapping("/update") public ResponseEntity<String> updateUser(@Validated(UpdateGroup.class) @RequestBody UserRegistrationRequest request) { // 處理用戶更新邏輯 return ResponseEntity.ok("用戶更新成功"); } }
在上述示例中,我們在updateUser
方法中使用@Validated(UpdateGroup.class)
來指定在更新場景中只執(zhí)行UpdateGroup
分組的驗證規(guī)則。由于username
字段上沒有指定groups
屬性,所以在更新場景中將不會對username
字段進行校驗。
自定義注解
Spring 的 validation 為我們提供了許多特性,幾乎可以滿足日常開發(fā)中絕大多數(shù)參數(shù)校驗場景了。但是,一個好的框架一定是方便擴展的。有了擴展能力,就能應對更多復雜的業(yè)務場景,下面我們自定義一個日期格式校驗的注解
定義注解接口
@Documented @Constraint(validatedBy = DateFormatValidator.class) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface DateFormat { //默認錯誤消息 String message() default "時間格式錯誤"; //分組 Class<?>[] groups() default {}; //默認日期格式 String formatter() default "yyyy-MM-dd"; //負載 Class<? extends Payload>[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { DateFormat[] value(); } }
定義校驗器
public class DateFormatValidator implements ConstraintValidator<DateFormat, String> { protected String dateFormatter; @Override public void initialize(DateFormat constraintAnnotation) { this.dateFormatter = constraintAnnotation.formatter(); } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (StringUtils.isNotBlank(value)) { try { DateUtils.parseDate(value, dateFormatter); } catch (Exception e) { return false; } return true; } return false; } }
自定義校驗注解使用起來和官方注解沒有區(qū)別,在需要的字段上添加相應注解即可。
public class RequestParam { @DateFormat(message = "日期輸入錯誤") private String beginDate; // 其他字段和方法 }
以上就是利用Spring Validation實現(xiàn)輸入驗證功能的詳細內(nèi)容,更多關于Spring Validation輸入驗證的資料請關注腳本之家其它相關文章!
相關文章
詳解Spring Data JPA中Repository的接口查詢方法
repository代理有兩種方式從方法名中派生出特定存儲查詢:通過直接從方法名派生查詢和通過使用一個手動定義的查詢。本文將通過示例詳細講解Spring Data JPA中Repository的接口查詢方法,需要的可以參考一下2022-04-04java中@JSONField和@JsonProperty注解的使用說明及對比
@JSONField與@JsonProperty隸屬兩個不同的包,前者是阿里系的fastjson包,后者是spring?boot官方使用的jackson包,本文主要介紹了java中@JSONField和@JsonProperty注解的使用說明及對比,感興趣的可以了解一下2023-11-11java獲取登錄者IP和登錄時間的兩種實現(xiàn)代碼詳解
這篇文章主要介紹了java獲取登錄者IP和登錄時間的實現(xiàn)代碼,本文通過兩種結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07