JSR303注解字段校验
JSR303是一套JavaBean参数校验的标准,定义了很多常用的校验注解
可以直接将这些注解加在我们JavaBean的属性上面就可以在需要校验的时候进行校验了
依赖
<!-- 属性效验--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>一、JSR303定义的校验类型
| @Null | 传参不包括该字段,或者传参该字段值为null |
| @NotNull | 字段必传,且字段值不能为null,String可为空字符串,Interger不能为空字符串(表单值为 “” 时,可以转换:Stirng为"",Integer为Null),一般加在Interger类型上 |
| @NotBlank | 字段必传,且字段值不能为null,去掉前后空格长度大于0(trim()),一般加在字符串上 |
| @NotEmpty | 字段必传,且字段值不能为null,加在字符串上效果同@NotBlank,也可以加在(Array,Collection,Map)上,判断不能null,一般加在集合、列表、Map上 |
| @AssertTrue | Boolean 成员变量的值只能为 true |
| @AssertFalse | Boolean 成员变量的值只能为 false |
| @Size(min=, max=) | 校验对象(Array,Collection,Map,String)长度是否在给定的范围之内,一般加在列表、集合、Map上 |
| @Length(min=, max=) | 校验字符串长度是否在指定范围内,只能加在字符串上 |
| @Past | 验证 Date 和 Calendar 对象是否在当前时间之前 |
| @Future | 验证 Date 和 Calendar 对象是否在当前时间之后 |
| @Pattern | 验证 String 对象是否符合正则表达式的规则 |
| @Min | Number 和 String 对象值大于等于指定的值 |
| @Max | Number 和 String 对象值小于等于指定的值 |
| @DecimalMax | 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度 |
| @DecimalMin | 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度 |
| @Digits | 验证 Number 和 String 的构成是否合法 |
| @Digits(integer=,fraction=) | 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。 |
| @Range(min=10000,max=50000,message=“range.bean.wage”) | private BigDecimal wage; |
| @CreditCardNumber | 信用卡验证 |
| 验证是否是邮件地址,如果为null,不进行验证,算通过验证。 | |
| @ScriptAssert(lang= ,script=, alias=) | |
| @URL(protocol=,host=, port=,regexp=, flags=) |
场景一:前端传过来的字段如何在后台做效验,最老的方法就是if else显得不是很灵活。如果前端传来100个字段就得写许多多余的代码。
第一个场景就是在后台创建的实体和前端传来的字段做对应映射,加上JSR303注解来做灵活的效验
1:给Bean实体添加校验注解:javax.validation.constraints(大部分注解都在这个包下),并定义自己的message提示如下:
二、在Springboot项目中使用
2.1、编写需要校验的Bean
package com.example.jsr.entity;import com.example.jsr.Constant; import lombok.Data; import org.hibernate.validator.constraints.Length;import javax.validation.constraints.*; import java.io.Serializable; import java.util.List;/*** @author Deyou Kong* @description 品牌实体类* @date 2023/2/25 9:16 上午*/@Data public class Brand implements Serializable {/*** ID*/private Integer id;/*** 品牌名称*/@NotBlank(message = Constant.NAME_NOT_NULL)@Length(min = 2, max = 32, message = "品牌长度为2-32")private String name;/*** 描述*/@NotNull(message = "描述不能为空NotNull")private String description;/*** 排序*/@Min(value = 1, message = "不能小于1")@Max(value = 10, message = "不能大于10")@NotNull(message = "排序不能为空")private Integer sort;@NotEmpty(message = "关联应用不能为空")@Size(min = 1, message = "品牌最少关联一个应用")private List<Integer> appList;}2.2、Controller方法中增加校验注解
@RestController @RequestMapping("/brand") public class BrandController {@Resourceprivate BrandService brandService;@PostMapping("/save")public JsonResult saveBrand(@Validated @RequestBody Brand brand, BindingResult bindingResult) {System.out.println("进入controller的save方法");if (bindingResult.hasErrors()) {//1.出现参数非法情况Map<String, String> map = new HashMap<>();bindingResult.getFieldErrors().forEach(fieldError -> {map.put(fieldError.getField(), fieldError.getDefaultMessage());});JsonResult jsonResult = JsonResult.fail("参数不正确,请检查");jsonResult.setData(map);return jsonResult;} else {//2.参数验证通过, 执行正常逻辑brandService.saveBrand(brand);return JsonResult.commonSuccess();}} }备注:这里一个@Validated (org.springframework.validation.annotation.Validated;)的参数后必须紧挨着一个BindingResult 参数接收参数效验的结果,否则spring会在校验不通过时直接抛出异常
2.3、统一异常处理
添加BindingResult参数后,虽然可以使后台在出现异常时,进行处理并返回统一的结果。但是我们会发现,我们写了许多与业务不相关的代码,为了解决这个问题,我们可以通过@ControllerAdvice进行异常的统一处理。
1、编写统一异常处理类
package com.example.jsr.advice;import com.example.jsr.entity.JsonResult; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.BindingResult; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.HashMap; import java.util.Map;/*** @author Deyou Kong* @description 全局异常捕获处理器* @date 2023/2/25 10:57 下午*/@RestControllerAdvice @Slf4j public class GlobalExceptionControllerAdvice {/*** 出现参数非法情况,抛出MethodArgumentNotValidException异常,在此捕获处理* @param e* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public JsonResult handlerMethodArgumentNotValidException(MethodArgumentNotValidException e){BindingResult bindingResult = e.getBindingResult();Map<String, String> map = new HashMap<>();bindingResult.getFieldErrors().forEach(fieldError -> {map.put(fieldError.getField(), fieldError.getDefaultMessage());});JsonResult jsonResult = JsonResult.fail("参数不正确,请检查");jsonResult.setData(map);return jsonResult;}/**兜底* @param e* @return*/@ExceptionHandler(Exception.class)public JsonResult handlerException(Exception e){JsonResult jsonResult = JsonResult.fail("未知的系统异常");jsonResult.setData(e.getMessage());return jsonResult;} }2、把之前加的BindingResult去掉,还原成原先最干净的代码
@RestController @RequestMapping("/brand") public class BrandController {@Resourceprivate BrandService brandService;@PostMapping("/save")public JsonResult saveBrand(@Validated @RequestBody Brand brand) {System.out.println("进入controller的save方法");brandService.saveBrand(brand);return JsonResult.commonSuccess();} }三、分组效验
在简单的数据验证中,我们使用完成了数据验证。但是还存在一些问题,如在添加品牌的时候Id为null,但在修改品牌的时候Id不能为null,这样的话,就冲突了。
那怎么办呢?我们可以给他们分个组,添加操作使用一组验证规则,修改操作使用一组验证规则。这就是分组验证的功能。
以@NotNull注解为例
@Constraint(validatedBy = { }) public @interface NotNull {String message() default "{javax.validation.constraints.NotNull.message}";//分组验证时使用Class<?>[] groups() default { };...我们通过@NotNul注解的groups指定属于哪个组
实现步骤:
1、创建AddGroup和UpdateGroup接口分别表示添加组和更新组
//这俩个接口只是用来标记的,不需要实现 public interface AddGroup { }public interface UpdateGroup { }2、实体类中使用注解时,标明该验证规则属于哪个组
@Data public class Brand implements Serializable {/*** ID*/@NotNull(message = "ID不能为空", groups = {UpdateGroup.class})private Integer id;/*** 品牌名称*/@NotBlank(message = Constant.NAME_NOT_NULL, groups = {AddGroup.class, UpdateGroup.class})@Length(min = 2, max = 32, message = "品牌长度为2-32", groups = {AddGroup.class, UpdateGroup.class})private String name;/*** 描述*/@NotNull(message = "描述不能为空NotNull")private String description;/*** 排序*/@Min(value = 1, message = "不能小于1")@Max(value = 10, message = "不能大于10")@NotNull(message = "排序不能为空")private Integer sort;@NotEmpty(message = "关联应用不能为空")@Size(min = 1, message = "品牌最少关联一个应用")private List<Integer> appList; }3、Controller中娇艳注解必须为@Validated
注意:
使用分组功能时,必须使用 @Validated 替代 @Valid,它支持分组效验功能
4、测试
1、上面实体类中 name 字段的@NotBlank、@Length两个注解对AddGroup、UpdateGroup两个分组都生效,所以测试结果中都有错误提示
2、ID字段只针对UpdateGroup分组生效,测试结果显示只针对update接口进行判断
3、其他字段的注解均未指定分组,那么在Controller指定分组的情况下,这些字段上面的校验注解不生效
四、自定义效验注解
步骤:
1、编写一个自定义的效验注解
2、编写一个自定义的效验器
3、关联自定义的效验器和自定义的效验注解
1、Brand实体类中增加一个字段
private String logo; // 需要判断logo地址是否以http开头2、、自定义 IsUrl 注解类
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @Constraint(validatedBy = {IsUrlValidator.class }) public @interface IsUrl {//JSR303规范中,要求必须有message、groups、payload这三个方法//default: 当message为null时,默认会到ValidationMessages.properties配置文件中找com.fcp.common.valid.ListValue.message的值String message() default "{com.fcp.common.valid.ListValue.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };// 注解中用户配置的字段值,如我指定url的规则存放哪个字段,这里存放在value字段中String value() default ""; }3、编写一个自定义的效验器IsUrlValidator.class
校验器的类名与注解类中Constraint中类名一致
4、在实体类中使用注解
@IsUrl(value = "https:", message = "URL地址不正确", groups = {AddGroup.class, UpdateGroup.class})@NotBlank(message = "logo不能为空")private String logo;5、测试
五、校验顺序
在测试过程中,发现多个注解校验顺序不定,这里还不知道怎么解决
看到一篇文章https://blog.csdn.net/qq_41762594/article/details/109326971,等后面有空在研究
总结
以上是生活随笔为你收集整理的JSR303注解字段校验的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Android仿朋友圈照片定点放大和滑动
- 下一篇: JSP前三章测试改错