JSR303校驗(yàn)前端傳遞的數(shù)據(jù)方式
介紹
JSR-303規(guī)范(Bean Validation規(guī)范)提供了對(duì) Java EE 和 Java SE 中的 Java Bean 進(jìn)行驗(yàn)證的方式。
該規(guī)范主要使用注解的方式來(lái)實(shí)現(xiàn)對(duì) Java Bean 的驗(yàn)證功能。
作用
前端傳遞數(shù)據(jù)到后端時(shí),可以使用其對(duì)Bean對(duì)象的屬性進(jìn)行合法性校驗(yàn)。
快速開(kāi)始
導(dǎo)入依賴
<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 = "密碼的長(zhǎng)度必須在6-20位")
@TableField("pass_word")
private String passWord;
@TableField("group_id")
private Long groupId;
@Max(value = 100, message = "城市編號(hào)不等大于100")
@TableField("city_id")
private Long cityId;
@Email(message = "郵件不合法")
@TableField("email")
private String email;
}統(tǒng)一返回結(jié)果
統(tǒng)一響應(yīng)結(jié)果枚舉類
@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
SUCCESS(0, "成功"),
ERROR(-1, "服務(wù)器內(nèi)部錯(cuò)誤"),
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);
}
/**
* 設(shè)置特定結(jié)果
*/
public static R setResult(ResponseEnum responseEnum) {
R r = new R();
r.setCode(responseEnum.getCode());
r.setMessage(responseEnum.getMessage());
return r;
}
/**
* 設(shè)置響應(yīng)消息
*/
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對(duì)象的下一個(gè)位置的參數(shù)寫(xiě)B(tài)indingResult對(duì)象,當(dāng)JSR303校驗(yàn)失敗后可以由BindResult對(duì)象捕獲異常
controller層
@Valid注解后面的對(duì)象是要校驗(yàn)的Bean對(duì)象
Bean對(duì)象的下一個(gè)位置的參數(shù)寫(xiě)B(tài)indingResult對(duì)象
@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<>();
// 判斷是否有錯(cuò)誤
if (bindingResult.hasErrors()) {
// 如果有錯(cuò)誤,遍歷錯(cuò)誤信息,添加到Map中
bindingResult.getFieldErrors().forEach((item) -> {
// 獲取錯(cuò)誤提示
String message = item.getDefaultMessage();
// 獲取錯(cuò)誤的屬性名稱
String field = item.getField();
map.put(field, message);
});
return R.error().message("參數(shù)信息錯(cuò)誤").data(map);
}
userService.save(user);
return R.ok();
}
}方法二
當(dāng)接口非常多,每一個(gè)接口都要寫(xiě)校驗(yàn)非常麻煩,寫(xiě)一個(gè)統(tǒng)一異常處理的類來(lái)集中處理異常
統(tǒng)一異常處理類
@Slf4j
@Component //Spring容易自動(dòng)管理
@RestControllerAdvice //在controller層添加通知。當(dāng)Controller層出現(xiàn)異常,這里的方法會(huì)捕獲異常,返回錯(cuò)誤信息(相當(dāng)于服務(wù)降級(jí))
public class UnifiedExceptionHandler {
/**
* 參數(shù)校驗(yàn)異常處理
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e) {
log.error("數(shù)據(jù)校驗(yàn)出現(xiàn)問(wè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ù)信息錯(cuò)誤").data(map);
}
}controller層
接口方法的參數(shù)如果有BindResult對(duì)象,代表校驗(yàn)出錯(cuò)由BindResult接受異常
接口方法的參數(shù)沒(méi)有BindResult對(duì)象,代表校驗(yàn)出錯(cuò)將拋出異常,被統(tǒng)一異常處理類的方法接受
接口方法的參數(shù)沒(méi)有BindResult對(duì)象,也沒(méi)有統(tǒng)一異常處理類的方法接受,就拋出400的異常
@PostMapping
public R addUser(@Valid @RequestBody User user) {
userService.save(user);
return R.ok();
}測(cè)試
校驗(yàn)成功的測(cè)試
請(qǐng)求體的JSON數(shù)據(jù)
{
"userName": "lixianchichi",
"passWord": "104ee44",
"groupId": 1,
"cityId": 14,
"email": "123456@qq.com"
}返回的響應(yīng)信息
{
"code": 0,
"message": "成功",
"data": {}
}校驗(yàn)失敗的測(cè)試
請(qǐng)求體的JSON數(shù)據(jù)
{
"userName": "lixianchichi",
"passWord": "10444eeeeeeeeeeeeeeeeee44",
"groupId": 1,
"cityId": 1514,
"email": "123456@qq.com"
}返回的響應(yīng)信息
{
"code": -1,
"message": "參數(shù)信息錯(cuò)誤",
"data": {
"passWord": "密碼的長(zhǎng)度必須在6-20位",
"cityId": "城市編號(hào)不等大于100"
}
}方法一與方法二測(cè)試結(jié)果相同,都為如上結(jié)果
分組校驗(yàn)
使用場(chǎng)景:不同情況下的校驗(yàn)規(guī)則是不同的,如新增的時(shí)候自動(dòng)生成Id,所以數(shù)據(jù)不需要攜帶Id,而修改的時(shí)候必須要攜帶Id(不同場(chǎng)景觸發(fā)不同的校驗(yàn)條件)
創(chuàng)建valid包
包里面創(chuàng)建兩個(gè)空接口InsertGroup和UpdateGroup,代表新增和修改兩種環(huán)境。
Java Bean
每一個(gè)Bean校驗(yàn)注解都有一個(gè)groups屬性,值是一個(gè)接口字節(jié)碼對(duì)象的數(shù)據(jù),用來(lái)指定環(huán)境。
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
@TableId
// 更新的時(shí)候校驗(yàn)
@NotNull(message = "修改用戶id不能為null" ,groups = {UpdateGroup.class})
// 新增的時(shí)候校驗(yàn)
@Null(message = "新增用戶id必須為null" ,groups = {InsertGroup.class})
private Long id;
// 新增和修改的時(shí)候都校驗(yàn)
@NotBlank(message = "用戶名不能為空" ,groups = {InsertGroup.class, UpdateGroup.class})
@TableField("user_name")
private String userName;
...
// 修改的時(shí)候判斷email是否合法,可以null,不報(bào)錯(cuò)(不傳就不校驗(yàn))
@Email(message = "郵件不合法" ,groups = {UpdateGroup.class})
@TableField("email")
private String email;
}controller層
使用@Validated代替@Valid注解,該注解可以指定環(huán)境(接口字節(jié)碼對(duì)象),
如下:代表當(dāng)前是新增的情況
@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {
userService.save(user);
return R.ok();
}注意:沒(méi)有指定groups屬性的注解,在controller層指定環(huán)境的情況下,不會(huì)生效
測(cè)試
在新增環(huán)境,前端傳遞JSON對(duì)象如果帶Id屬性
@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {
userService.save(user);
return R.ok();
}響應(yīng)結(jié)果
{
"code": -1,
"message": "參數(shù)信息錯(cuò)誤",
"data": {
"id": "新增用戶id必須為null"
}
}在修改環(huán)境,前端傳遞JSON對(duì)象如果帶Id屬性
@PostMapping
public R addUser(@Validated({UpdateGroup.class}) @RequestBody User user) {
userService.save(user);
return R.ok();
}響應(yīng)結(jié)果
{
"code": -1,
"message": "參數(shù)信息錯(cuò)誤",
"data": {
"id": "修改用戶id不能為null"
}
}自定義校驗(yàn)注解
實(shí)現(xiàn)功能:校驗(yàn)Bean的某個(gè)屬性的字段只能是0和1
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
...
@TableField("group_id")
@ListValue(vals = {0L, 1L}, groups = {UpdateGroup.class})
private Long groupId;
...
}自定義的校驗(yàn)注解
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 指定校驗(yàn)器,可以指定多個(gè)校驗(yàn)器,會(huì)自動(dòng)適配,如:此注解還有一個(gè)Double數(shù)組的屬性,再添加一個(gè)Double類型的校驗(yàn)器,當(dāng)我們使用注解的時(shí)候,我們給Double數(shù)組賦值,就會(huì)自動(dòng)找Double類型的校驗(yàn)器校驗(yàn)
@Constraint(validatedBy = {ListValueCondtraintValidator.class})
public @interface ListValue {
// 前三個(gè)屬性都是每個(gè)JSR303注解必須有的
// 默認(rèn)錯(cuò)誤信息,從properties里獲取值
String message() default "{com.lixianhe.valid.listValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
long[] vals() default {};
}自定義校驗(yàn)器
/**
* 自定義校驗(yàn)器
*/
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);
}
}
/**
* 判斷是否校驗(yàn)成功
*
* @param value 需要校驗(yàn)的值
* @param constraintValidatorContext
* @return 校驗(yàn)結(jié)果
*/
@Override
public boolean isValid(Long value, ConstraintValidatorContext constraintValidatorContext) {
return set.contains(value);
}
}測(cè)試
當(dāng)groupId傳2的時(shí)候
{
"userName": "lixianchichi",
"passWord": "104eefd454545546456565tygpl[per44",
"groupId": 2,
"cityId": 10
}響應(yīng)結(jié)果
{
"code": -1,
"message": "參數(shù)信息錯(cuò)誤",
"data": {
"groupId": "必須提交指定的值"
}
}補(bǔ)充
Java Bean校驗(yàn)注解總結(jié)
| 限制 | 說(shuō)明 |
|---|---|
| @Null | 限制只能為null |
| @NotNull | 限制必須不為null |
| @AssertFalse | 限制必須為false |
| @AssertTrue | 限制必須為true |
| @DecimalMax(value) | 限制必須為一個(gè)不大于指定值的數(shù)字 |
| @DecimalMin(value) | 限制必須為一個(gè)不小于指定值的數(shù)字 |
| @Digits(integer,fraction) | 限制必須為一個(gè)小數(shù),且整數(shù)部分的位數(shù)不能超過(guò)integer,小數(shù)部分的位數(shù)不能超過(guò)fraction |
| @Future | 限制必須是一個(gè)將來(lái)的日期 |
| @Max(value) | 限制必須為一個(gè)不大于指定值的數(shù)字 |
| @Min(value) | 限制必須為一個(gè)不小于指定值的數(shù)字 |
| @Past | 限制必須是一個(gè)過(guò)去的日期 |
| @Pattern(value) | 限制必須符合指定的正則表達(dá)式 |
| @Size(max,min) | 限制字符長(zhǎng)度必須在min到max之間 |
| @Past | 驗(yàn)證注解的元素值(日期類型)比當(dāng)前時(shí)間早 |
| @NotEmpty | 驗(yàn)證注解的元素值不為null且不為空(字符串長(zhǎng)度不為0、集合大小不為0) |
| @NotBlank | 驗(yàn)證注解的元素值不為空(不為null、去除首位空格后長(zhǎng)度為0),不同于@NotEmpty,@NotBlank只應(yīng)用于字符串且在比較時(shí)會(huì)去除字符串的空格 |
| 驗(yàn)證注解的元素值是Email,也可以通過(guò)正則表達(dá)式和flag指定自定義的email格式 | |
| @URL | 校驗(yàn)是否位合法的URL |
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring線程池ThreadPoolExecutor配置并且得到任務(wù)執(zhí)行的結(jié)果
今天小編就為大家分享一篇關(guān)于Spring線程池ThreadPoolExecutor配置并且得到任務(wù)執(zhí)行的結(jié)果,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Springboot整合第三方登錄功能的實(shí)現(xiàn)示例
本文主要介紹了Springboot整合第三方登錄功能的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
SpringBoot整合Dubbo框架,實(shí)現(xiàn)RPC服務(wù)遠(yuǎn)程調(diào)用
Dubbo是一款高性能、輕量級(jí)的開(kāi)源Java RPC框架,它提供了三大核心能力:面向接口的遠(yuǎn)程方法調(diào)用,智能容錯(cuò)和負(fù)載均衡,以及服務(wù)自動(dòng)注冊(cè)和發(fā)現(xiàn)。今天就來(lái)看下SpringBoot整合Dubbo框架的步驟2021-06-06
java 串口通信詳細(xì)及簡(jiǎn)單實(shí)例
這篇文章主要介紹了java 串口通信詳細(xì)及簡(jiǎn)單實(shí)例的相關(guān)資料,在開(kāi)發(fā)硬件與軟件結(jié)合的時(shí)候,就會(huì)用到串口,需要的朋友可以參考下2017-01-01
關(guān)于Java 中的 Lambda 表達(dá)式
這篇文章主要介紹了關(guān)于Java 中的 Lambda 表達(dá)式,Lambda 表達(dá)式是 Java 涉足函數(shù)式編程的過(guò)程。它接受參數(shù)并將其應(yīng)用于表達(dá)式或代碼塊,下面一起進(jìn)入文章查看詳細(xì)內(nèi)容2021-11-11
關(guān)于springboot的跨域配置問(wèn)題的解決方案
這篇文章主要介紹了關(guān)于springboot的跨域配置問(wèn)題,處理filter,spring?security等過(guò)濾器跨域問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
Eclipse轉(zhuǎn)Itellij IDEA導(dǎo)入Git/svn本地項(xiàng)目的詳細(xì)步驟
這篇文章主要介紹了Eclipse轉(zhuǎn)Itellij IDEA導(dǎo)入Git/svn本地項(xiàng)目,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

