javascript
学习Spring Boot:(十)使用hibernate validation完成数据后端校验
前言
后臺數據的校驗也是開發中比較注重的一點,用來校驗數據的正確性,以免一些非法的數據破壞系統,或者進入數據庫,造成數據污染,由于數據檢驗可能應用到很多層面,所以系統對數據校驗要求比較嚴格且追求可變性及效率。
了解
了解一點概念性的東東。
* JSR 303 是 Java 為 Bean 數據合法性校驗提供的標準框架,它已經包含在 JavaEE 6.0 中 。
* Hibernate Validator 是 JSR 303 的一個參考實現,所以它多實現了幾個校驗規則。
* Spring 4.0 擁有自己獨立的數據校驗框架,同時支持 JSR303 標準的校驗框架。
* 在已經標注了 JSR303 注解的表單/命令對象前標注一個@Valid,Spring MVC 框架在將請求參數綁定到該入參對象后,就會調用校驗框架根據注解聲明的校驗規則實施校驗
* Spring MVC 是通過對處理方法簽名的規約來保存校驗結果的:前一個表單/命令對象的校驗結果保存到隨后的入參中,這個保存校驗結果的入參必須是 BindingResult 或Errors 類型,這兩個類都位于org.springframework.validation 包中。
* 需校驗的 Bean 對象和其綁定結果對象或錯誤對象時成對出現的,它們之間不允許聲明其他的入參
* Errors 接口提供了獲取錯誤信息的方法,如 getErrorCount() 或getFieldErrors(String field)
* BindingResult 擴展了 Errors 接口。
支持的注解
JSR 提供的校驗注解:
@Null 被的注解元素必須為 null @NotNull 被注解的元素必須不為 null @AssertTrue 被注解的元素必須為 true @AssertFalse 被注解的元素必須為 false @Min(value) 被注解的元素必須是一個數字,其值必須大于等于指定的最小值 @Max(value) 被注解的元素必須是一個數字,其值必須小于等于指定的最大值 @DecimalMin(value) 被注解的元素必須是一個數字,其值必須大于等于指定的最小值 @DecimalMax(value) 被注解的元素必須是一個數字,其值必須小于等于指定的最大值 @Size(max=, min=) 被注解的元素的大小必須在指定的范圍內 集合或數組 集合或數組的大小是否在指定范圍內 @Digits (integer, fraction) 被注解的元素必須是一個數字,驗證是否是符合指定格式的數字,interger指定整數精度,fraction指定小數精度。 @Past 被注解的元素必須是一個過去的日期 @Future 被注解的元素必須是一個將來的日期 @Pattern(regex=,flag=) 被注解的元素必須符合指定的正則表達式Hibernate Validator 提供的校驗注解:
@NotBlank(message =) 驗證字符串非null,且長度必須大于0 @Email 被注釋的元素必須是電子郵箱地址 @Length(min=,max=) 被注解的值大小必須在指定的范圍內 @NotEmpty 被注解的字符串的必須非空 @Range(min=,max=,message=) 驗證該值必須在合適的范圍內可以在需要驗證的屬性上,使用多個驗證方式,它們同時生效。
spring boot web 已經有 hibernate-validation 的依賴,所以不需要再手動添加依賴。
使用
首先我在我的實體類上寫了幾個校驗注解。
public class SysUserEntity implements Serializable {private static final long serialVersionUID = 1L;//主鍵private Long id;//用戶名@NotBlank(message = "用戶名不能為空", groups = {AddGroup.class, UpdateGroup.class})private String username;//密碼@NotBlank(message = "密碼不能為空", groups = {AddGroup.class})private String password;//手機號@Pattern(regexp = "^1([345789])\\d{9}$",message = "手機號碼格式錯誤")@NotBlank(message = "手機號碼不能為空")private String mobile;//郵箱@Email(message = "郵箱格式不正確")private String email;//創建者private Long createUserId;//創建時間private Date createDate; // ignore set and get使用@Validated進行校驗
首先了解下:
關于@Valid和@Validated的區別聯系
* @Valid: javax.validation, 是javax,也是就是jsr303中定義的規范注解
* @Validated: org.springframework.validation.annotation, 是spring自己封裝的注解。參數校驗失敗拋出 org.springframework.validation.BindException 異常。
@Validated 是 @Valid 的一個變種,擴展了 @Valid 的功能,支持 group分組校驗 的寫法,所以為了校驗統一,盡量使用 @Validated
在controller自定義一個接口
@PostMapping("/valid")public ResponseEntity<String> valid(@Validated @RequestBody SysUserEntity user, BindingResult result) {if (result.hasErrors()) {return ResponseEntity.status(BAD_REQUEST).body("校驗失敗");}return ResponseEntity.status(OK).body("校驗成功");}需要注意的有幾點:
* 需要校驗對象的時候,需要加上 spring 的校驗注解 @Validated ,表示我們需要 spring 對它進行校驗,而校驗的信息會存放到其后的BindingResult中。
* BindingResult 必須和檢驗對象緊鄰,中間不能穿插任何參數,如果有多個校驗對象 @Validated @RequestBody SysUserEntity user, BindingResult result, @Validated @RequestBody SysUserEntity user1, BindingResult result1。
我在前端用 Swagger 進行測試下。
我發送一個 body,將 手機號輸錯:
后端調試下 BindingResult 的結果,發現結果:
只要注意下 errors 屬性,它是校驗所有不符合規則的,是一個數組。
分組校驗
有時候 ,我們在新增和更新的時候校驗效果是不一樣的。例如上面,我在User新增的時候需要判斷密碼是不是為空,但是更新的時候我不做校驗。這個時候就也要用到分組校驗了。
@NotBlank(message = "密碼不能為空", groups = {AddGroup.class}) private String password;將Contoller中的校驗修改下。
(@Validated({AddGroup.class}) @RequestBody SysUserEntity user, BindingResult result)上面的意思是只有分組是AddGroup的校驗才生效,其余的校驗忽略。
經過我測試,把分組情況分下:
1. 在controller校驗沒加分組的時候,只對實體類的沒有分組的注解有效。
2. 在controller校驗加分組的時候,只對實體類的當前分組的注解有效,沒有注解的也無效。
3. 當校驗有兩個分組的時候@Validated({AddGroup.class, UpdateGroup.class}),滿足當前兩個分組其中任意一個都可以校驗,兩個注解同時一起出現,也沒問題,而且檢驗不通過的信息不會重復。
自定義校驗
有時候系統提供給我們的校驗注解,并不夠用,我們可以自定義校驗,來滿足我們的業務需求。
例如:現在我們有一個需求,需要檢測一條信息的敏感詞匯,如sb ……文明人,舉個栗子 ……
自定義校驗注解
// 注解可以用在哪些地方 @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented // 指定校驗規則實現類 @Constraint(validatedBy = {NotHaveSBValidator.class}) public @interface NotHaveSB {//默認錯誤消息String message() default "不能包含字符sb";//分組Class<?>[] groups() default {};//負載Class<? extends Payload>[] payload() default {};//指定多個時使用@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})@Retention(RUNTIME)@Documented@interface List {NotHaveSB[] value();}}規則校驗實現類
// 可以指定檢驗類型,這里選擇的是 String public class NotHaveSBValidator implements ConstraintValidator<NotHaveSB, String> {@Overridepublic void initialize(NotHaveSB notHaveSB) {}/**** @param s 待檢驗對象* @param constraintValidatorContext 檢驗上下文,可以設置檢驗的錯誤信息* @return false 代表檢驗失敗*/@Overridepublic boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {return !StringUtils.isNotBlank(s) || !s.toLowerCase().contains("sb");} }所有的驗證者都需要實現ConstraintValidator接口,它的接口也很形象,包含一個初始化事件方法,和一個判斷是否合法的方法。
測試一下喂
現在我的用戶類上,也沒什么多余的字段拿出來測試,暫時把 password 字段拿來測試吧。
//@NotBlank(message = "密碼不能為空", groups = AddGroup.class)@NotHaveSBprivate String password;手動校驗
這個是我最終想要的處理方式。
由于現在都是前后端分離開發的,校驗失敗的時候,拋出自定義的異常,然后統一處理這些異常,最后將相關的錯誤提示信息返回給前端處理。
新建一個驗證工具類
public class ValidatorUtils {private static Validator validator;static {validator = Validation.buildDefaultValidatorFactory().getValidator();}/*** 手動校驗對象** @param object 待校驗對象* @param groups 待校驗的組* @throws KCException 校驗不通過,則拋出 KCException 異常*/public static void validateEntity(Object object, Class<?>... groups)throws KCException {Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);if (!constraintViolations.isEmpty()) {String msg = constraintViolations.parallelStream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));throw new KCException(msg);}} }它主要做的事情就是驗證我們的待驗證對象,驗證不同通過的時候,拋出自定義異常,在后臺統一處理異常就可以了。
在業務中直接調用就可以了,有分組添加分組就行:
@PostMapping("/valid1")public ResponseEntity<String> customValid(@RequestBody SysUserEntity user) {ValidatorUtils.validateEntity(user);return ResponseEntity.status(OK).body("校驗成功");}最后測試一下,查看返回結果是否符合預期:
手動校驗的補充
決定還是采用注解的形式進行編碼,本來想用處理方法參數的裝配進行檢驗,寫好了發現和 @responseBody 不能同時使用,然后發現還是可以使用 @Validated 直接校驗,拋出異常, 進行捕捉異常統一處理。
@PostMapping()@ApiOperation("新增")public ResponseEntity insert(@Validated SysUserAddForm user)在全局異常處理里面加上 處理綁定參數異常 org.springframework.validation.BindException:
/*** 參數檢驗違反約束(數據校驗)* @param e BindException* @return error message*/@org.springframework.web.bind.annotation.ExceptionHandler(BindException.class)public ResponseEntity<String> handleConstraintViolationException(BindException e) {LOGGER.debug(e.getMessage(), e);return ResponseEntity.status(BAD_REQUEST).body(e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(",")));}轉載于:https://www.cnblogs.com/qnight/p/8997498.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的学习Spring Boot:(十)使用hibernate validation完成数据后端校验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vmware克隆虚拟机
- 下一篇: Box-Cox(python实现)