JSR303校驗前端傳遞的數(shù)據(jù)方式
介紹
JSR-303規(guī)范(Bean Validation規(guī)范)提供了對 Java EE 和 Java SE 中的 Java Bean 進行驗證的方式。
該規(guī)范主要使用注解的方式來實現(xiàn)對 Java Bean 的驗證功能。
作用
前端傳遞數(shù)據(jù)到后端時,可以使用其對Bean對象的屬性進行合法性校驗。
快速開始
導入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
Java Bean
@Data @AllArgsConstructor @NoArgsConstructor @TableName("user") public class User { @TableId private Long id; @NotBlank(message = "用戶名不能為空") @TableField("user_name") private String userName; @Size(max = 20,min = 6,message = "密碼的長度必須在6-20位") @TableField("pass_word") private String passWord; @TableField("group_id") private Long groupId; @Max(value = 100, message = "城市編號不等大于100") @TableField("city_id") private Long cityId; @Email(message = "郵件不合法") @TableField("email") private String email; }
統(tǒng)一返回結(jié)果
統(tǒng)一響應結(jié)果枚舉類
@Getter @AllArgsConstructor @ToString public enum ResponseEnum { SUCCESS(0, "成功"), ERROR(-1, "服務器內(nèi)部錯誤"), private final Integer code; private final String message; }
統(tǒng)一結(jié)果返回類
@Data public class R { private Integer code; private String message; /** * 返回的數(shù)據(jù) */ private Map<String, Object> data = new HashMap<>(); private R() { } public static R ok() { return setResult(ResponseEnum.SUCCESS); } public static R error() { return setResult(ResponseEnum.ERROR); } /** * 設置特定結(jié)果 */ public static R setResult(ResponseEnum responseEnum) { R r = new R(); r.setCode(responseEnum.getCode()); r.setMessage(responseEnum.getMessage()); return r; } /** * 設置響應消息 */ public R message(String message) { this.setMessage(message); return this; } public R code(Integer code) { this.setCode(code); return this; } public R data(String key, Object value) { this.data.put(key, value); return this; } public R data(Map<String, Object> map) { this.setData(map); return this; } }
方法一
Bean對象的下一個位置的參數(shù)寫B(tài)indingResult對象,當JSR303校驗失敗后可以由BindResult對象捕獲異常
controller層
@Valid注解后面的對象是要校驗的Bean對象
Bean對象的下一個位置的參數(shù)寫B(tài)indingResult對象
@RestController @RequestMapping("/user") public class UserController { @Resource private UserServiceImpl userService; @PostMapping public R addUser(@Valid @RequestBody User user, BindingResult bindingResult) { Map<String, Object> map = new HashMap<>(); // 判斷是否有錯誤 if (bindingResult.hasErrors()) { // 如果有錯誤,遍歷錯誤信息,添加到Map中 bindingResult.getFieldErrors().forEach((item) -> { // 獲取錯誤提示 String message = item.getDefaultMessage(); // 獲取錯誤的屬性名稱 String field = item.getField(); map.put(field, message); }); return R.error().message("參數(shù)信息錯誤").data(map); } userService.save(user); return R.ok(); } }
方法二
當接口非常多,每一個接口都要寫校驗非常麻煩,寫一個統(tǒng)一異常處理的類來集中處理異常
統(tǒng)一異常處理類
@Slf4j @Component //Spring容易自動管理 @RestControllerAdvice //在controller層添加通知。當Controller層出現(xiàn)異常,這里的方法會捕獲異常,返回錯誤信息(相當于服務降級) public class UnifiedExceptionHandler { /** * 參數(shù)校驗異常處理 */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public R handleValidException(MethodArgumentNotValidException e) { log.error("數(shù)據(jù)校驗出現(xiàn)問題{}, 異常類型{}", e.getMessage(), e.getClass()); BindingResult bindingResult = e.getBindingResult(); Map<String, Object> map = new HashMap<>(); bindingResult.getFieldErrors().forEach((item) -> { map.put(item.getField(), item.getDefaultMessage()); }); return R.error().message("參數(shù)信息錯誤").data(map); } }
controller層
接口方法的參數(shù)如果有BindResult對象,代表校驗出錯由BindResult接受異常
接口方法的參數(shù)沒有BindResult對象,代表校驗出錯將拋出異常,被統(tǒng)一異常處理類的方法接受
接口方法的參數(shù)沒有BindResult對象,也沒有統(tǒng)一異常處理類的方法接受,就拋出400的異常
@PostMapping public R addUser(@Valid @RequestBody User user) { userService.save(user); return R.ok(); }
測試
校驗成功的測試
請求體的JSON數(shù)據(jù)
{ "userName": "lixianchichi", "passWord": "104ee44", "groupId": 1, "cityId": 14, "email": "123456@qq.com" }
返回的響應信息
{ "code": 0, "message": "成功", "data": {} }
校驗失敗的測試
請求體的JSON數(shù)據(jù)
{ "userName": "lixianchichi", "passWord": "10444eeeeeeeeeeeeeeeeee44", "groupId": 1, "cityId": 1514, "email": "123456@qq.com" }
返回的響應信息
{ "code": -1, "message": "參數(shù)信息錯誤", "data": { "passWord": "密碼的長度必須在6-20位", "cityId": "城市編號不等大于100" } }
方法一與方法二測試結(jié)果相同,都為如上結(jié)果
分組校驗
使用場景:不同情況下的校驗規(guī)則是不同的,如新增的時候自動生成Id,所以數(shù)據(jù)不需要攜帶Id,而修改的時候必須要攜帶Id(不同場景觸發(fā)不同的校驗條件)
創(chuàng)建valid包
包里面創(chuàng)建兩個空接口InsertGroup和UpdateGroup,代表新增和修改兩種環(huán)境。
Java Bean
每一個Bean校驗注解都有一個groups屬性,值是一個接口字節(jié)碼對象的數(shù)據(jù),用來指定環(huán)境。
@Data @AllArgsConstructor @NoArgsConstructor @TableName("user") public class User { @TableId // 更新的時候校驗 @NotNull(message = "修改用戶id不能為null" ,groups = {UpdateGroup.class}) // 新增的時候校驗 @Null(message = "新增用戶id必須為null" ,groups = {InsertGroup.class}) private Long id; // 新增和修改的時候都校驗 @NotBlank(message = "用戶名不能為空" ,groups = {InsertGroup.class, UpdateGroup.class}) @TableField("user_name") private String userName; ... // 修改的時候判斷email是否合法,可以null,不報錯(不傳就不校驗) @Email(message = "郵件不合法" ,groups = {UpdateGroup.class}) @TableField("email") private String email; }
controller層
使用@Validated代替@Valid注解,該注解可以指定環(huán)境(接口字節(jié)碼對象),
如下:代表當前是新增的情況
@PostMapping public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) { userService.save(user); return R.ok(); }
注意:沒有指定groups屬性的注解,在controller層指定環(huán)境的情況下,不會生效
測試
在新增環(huán)境,前端傳遞JSON對象如果帶Id屬性
@PostMapping public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) { userService.save(user); return R.ok(); }
響應結(jié)果
{ "code": -1, "message": "參數(shù)信息錯誤", "data": { "id": "新增用戶id必須為null" } }
在修改環(huán)境,前端傳遞JSON對象如果帶Id屬性
@PostMapping public R addUser(@Validated({UpdateGroup.class}) @RequestBody User user) { userService.save(user); return R.ok(); }
響應結(jié)果
{ "code": -1, "message": "參數(shù)信息錯誤", "data": { "id": "修改用戶id不能為null" } }
自定義校驗注解
實現(xiàn)功能:校驗Bean的某個屬性的字段只能是0和1
@Data @AllArgsConstructor @NoArgsConstructor @TableName("user") public class User { ... @TableField("group_id") @ListValue(vals = {0L, 1L}, groups = {UpdateGroup.class}) private Long groupId; ... }
自定義的校驗注解
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented // 指定校驗器,可以指定多個校驗器,會自動適配,如:此注解還有一個Double數(shù)組的屬性,再添加一個Double類型的校驗器,當我們使用注解的時候,我們給Double數(shù)組賦值,就會自動找Double類型的校驗器校驗 @Constraint(validatedBy = {ListValueCondtraintValidator.class}) public @interface ListValue { // 前三個屬性都是每個JSR303注解必須有的 // 默認錯誤信息,從properties里獲取值 String message() default "{com.lixianhe.valid.listValue.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; long[] vals() default {}; }
自定義校驗器
/** * 自定義校驗器 */ public class ListValueCondtraintValidator implements ConstraintValidator<ListValue, Long> { private Set<Long> set = new HashSet<>(); /** * 初始化方法 * * @param constraintAnnotation */ @Override public void initialize(ListValue constraintAnnotation) { long[] vals = constraintAnnotation.vals(); for (long val : vals) { set.add(val); } } /** * 判斷是否校驗成功 * * @param value 需要校驗的值 * @param constraintValidatorContext * @return 校驗結(jié)果 */ @Override public boolean isValid(Long value, ConstraintValidatorContext constraintValidatorContext) { return set.contains(value); } }
測試
當groupId傳2的時候
{ "userName": "lixianchichi", "passWord": "104eefd454545546456565tygpl[per44", "groupId": 2, "cityId": 10 }
響應結(jié)果
{ "code": -1, "message": "參數(shù)信息錯誤", "data": { "groupId": "必須提交指定的值" } }
補充
Java Bean校驗注解總結(jié)
限制 | 說明 |
---|---|
@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格式 | |
@URL | 校驗是否位合法的URL |
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java實現(xiàn)鏈表數(shù)據(jù)結(jié)構(gòu)的方法
這篇文章主要介紹了Java實現(xiàn)鏈表數(shù)據(jù)結(jié)構(gòu)的相關資料,每一個鏈表都包含多個節(jié)點,節(jié)點又包含兩個部分,一個是數(shù)據(jù)域(儲存節(jié)點含有的信息),一個是引用域(儲存下一個節(jié)點或者上一個節(jié)點的地址),需要的朋友可以參考下2022-01-01Java Hutool 包工具類推薦 ExcelUtil詳解
這篇文章主要介紹了Java Hutool 包工具類推薦 ExcelUtil詳解,需要引入hutool包,版本號可根據(jù)實際情況更換,除hutool包之外,還需要引入操作Excel必要包,本文給大家介紹的非常詳細,需要的朋友可以參考下2022-09-09Java Swing JSlider滑塊的實現(xiàn)示例
這篇文章主要介紹了Java Swing JSlider滑塊的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12詳細分析Java并發(fā)集合LinkedBlockingQueue的用法
這篇文章主要介紹了詳細分析Java并發(fā)集合LinkedBlockingQueue的用法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04SpringBoot實現(xiàn)動態(tài)增刪啟停定時任務的方式
在spring?boot中,可以通過@EnableScheduling注解和@Scheduled注解實現(xiàn)定時任務,也可以通過SchedulingConfigurer接口來實現(xiàn)定時任務,但是這兩種方式不能動態(tài)添加、刪除、啟動、停止任務,本文給大家介紹SpringBoot實現(xiàn)動態(tài)增刪啟停定時任務的方式,感興趣的朋友一起看看吧2024-03-03