JSR303后端校验详解

JSR303后端校验详解前言 数据校验是用来判断输入的数据是否满足规定的要求的 前端可用 JS 来校验 如用户名唯一性 生日格式 邮箱格式校验等 前端做了数据校验 后端也做数据校验的原因是防止有人绕过前端界面 直接向后端发起请求 导致数据库内写入了脏数据 常用注解 JSR 提供的校验注解

大家好,我是讯享网,很高兴认识大家。

前言

数据校验是用来判断输入的数据是否满足规定的要求的。
前端可用JS来校验,如用户名唯一性,生日格式,邮箱格式校验等。
前端做了数据校验,后端也做数据校验的原因是防止有人绕过前端界面,直接向后端发起请求,导致数据库内写入了脏数据。

常用注解

JSR提供的校验注解: @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null @AssertTrue 被注释的元素必须为 true @AssertFalse 被注释的元素必须为 false @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @Size(max=, min=) 被注释的元素的大小必须在指定的范围内 @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内 @Past 被注释的元素必须是一个过去的日期 @Future 被注释的元素必须是一个将来的日期 @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式 Hibernate Validator提供的校验注解: @NotBlank(message =) 验证字符串非null,且长度必须大于0 @Email 被注释的元素必须是电子邮箱地址 @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内 @NotEmpty 被注释的字符串的必须非空 @Range(min=,max=,message=) 被注释的元素必须在合适的范围内 

讯享网

实战

1、添加校验注解

在属性上添加校验注解,用于校验输入的属性是否符合校验规则。

还可以在校验注解上自定义校验失败后显示的信息。如@Null(message = “新增时不能指定id”),当校验失败后,错误信息展示为message中内容。

讯享网/ * 品牌 * * @author xiao-wang * @email * @date 2022-03-09 10:19:41 */ @Data @TableName("pms_brand") public class BrandEntity implements Serializable { 
    private static final long serialVersionUID = 1L; / * 品牌id */ @TableId @Null(message = "新增时不能指定id") private Long brandId; / * 品牌名 */ @NotBlank(message = "品牌名不能为空") private String name; / * 品牌logo地址 */ @NotBlank @URL(message = "url地址必须是一个有效的地址") private String logo; / * 介绍 */ private String descript; / * 显示状态[0-不显示;1-显示] */ // @TableLogic(value = "1",delval = "0") @NotNull(message = "显示状态不能为空") private Integer showStatus; / * 检索首字母 */ @NotBlank @Pattern(regexp = "^[a-zA-Z]$",groups = { 
   message = "首字母必须为a-z或者A-Z中的一个字母") private String firstLetter; / * 排序 */ @NotNull private Integer sort; } 

2、在controller层添加@valid注解
只添加校验注解,校验是不会生效的。在controller层的某个方法上添加了@valid注解,校验才会生效。

@RestController @RequestMapping("product/brand") public class BrandController { 
    @Autowired private BrandService brandService; @RequestMapping("/save") public R save(@Valid @RequestBody BrandEntity brand){ 
    brandService.save(brand); return R.ok(); } } 

用postman测试,校验完成,出现提示信息。

讯享网{ 
    "timestamp": "2022-03-31 02:15:05", "status": 400, "error": "Bad Request", "errors": [ { 
    "codes": [ "NotBlank.brandEntity.name", "NotBlank.name", "NotBlank.java.lang.String", "NotBlank" ], "arguments": [ { 
    "codes": [ "brandEntity.name", "name" ], "arguments": null, "defaultMessage": "name", "code": "name" } ], "defaultMessage": "品牌名不能为空", "objectName": "brandEntity", "field": "name", "rejectedValue": "", "bindingFailure": false, "code": "NotBlank" } ], "message": "Validation failed for object='brandEntity'. Error count: 6", "path": "/product/brand/save" } 

但是返回的格式不是自己需要的。

要求返回的格式

{ 
    "msg": "提交数据不合法", "code": 400, "data": { 
    "校验失败属性名": "错误提示信息", } } 

3、统一异常处理

为了规范异常返回的格式,返回一个R类型,R类型的格式为上述要求返回的格式


讯享网

避免代码的冗余,新建一个类用来统一异常处理。

讯享网@Slf4j @RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller") public class GulimallExceptionController { 
    @ExceptionHandler(value = MethodArgumentNotValidException.class) //表示能捕获的异常类型为MethodArgumentNotValidException public R methodArgumentValidException(MethodArgumentNotValidException e){ 
    log.error("数据校验出现问题{},异常类型{}",e.getMessage(),e.getClass()); BindingResult bindingResult = e.getBindingResult(); //e.getBindingResult()表示校验结果 Map<String,String> errorsMap = new HashMap<>(); bindingResult.getFieldErrors().forEach((item)->{ 
    //遍历校验结果中的错误信息 String field = item.getField(); //错误信息中的报错的属性名 String message = item.getDefaultMessage(); //报错的错误提示 errorsMap.put(field,message); }); return R.error(400,"提交数据不合法").put("data",errorsMap); } } 

分组校验

有的属性在新增的时候必须为空,但是修改的时候必须不为空

所以,需要对属性进行分组校验。

1、创建好分组接口,并在实体类的属性上加上groups=哪个分组

@Data @TableName("pms_brand") public class BrandEntity implements Serializable { 
    private static final long serialVersionUID = 1L; / * 品牌id */ @TableId @Null(message = "新增时不能指定id",groups = { 
   AddGroup.class}) @NotNull(message = "修改时必须指定id",groups = { 
   UpdateGroup.class}) private Long brandId; / * 品牌名 */ @NotBlank(message = "品牌名不能为空",groups = { 
   AddGroup.class}) private String name; / * 品牌logo地址 */ @NotBlank(groups = { 
   AddGroup.class}) @URL(message = "url地址必须是一个有效的地址",groups = { 
   AddGroup.class,UpdateGroup.class}) private String logo; / * 介绍 */ private String descript; / * 显示状态[0-不显示;1-显示] */ // @TableLogic(value = "1",delval = "0") @NotNull(groups = { 
   AddGroup.class},message = "显示状态不能为空") @ListValues(values = { 
   0,1},groups = { 
   AddGroup.class}) private Integer showStatus; / * 检索首字母 */ @NotBlank(groups = { 
   AddGroup.class}) @Pattern(regexp = "^[a-zA-Z]$",groups = { 
   AddGroup.class, UpdateGroup.class},message = "首字母必须为a-z或者A-Z中的一个字母") private String firstLetter; / * 排序 */ @NotNull(groups = { 
   AddGroup.class}) private Integer sort; } 

2、controller层需要将@valid注解换成@Validated注解

由上可知,只添加注解是不会生效的,需要在controller层的方法内添加@Valid注解,但是@Valid注解没有分组功能,因此,换成@Validated注解,指定生效的是哪个分组。

讯享网@RestController @RequestMapping("product/brand") public class BrandController { 
    @Autowired private BrandService brandService; @RequestMapping("/save") public R save(@Validated(value = { 
   AddGroup.class}) @RequestBody BrandEntity brand){ 
    brandService.save(brand); return R.ok(); } } 

自定义校验

1、自定义校验注解
自己添加一个注解,规定该属性值只能填入规定的0和1,否则会报错
在这里插入图片描述

@Constraint(validatedBy = { 
   ListvaluesConstraintValidator.class}) //指定该注解由ListvaluesConstraintValidator.class来校验 @Target({ 
   ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface ListValues { 
    String message() default "{com.atguigu.common.valid.ListValues.message}"; //在resources中新建ValidationMessages.properties(必须为此名字),就可指定该注解的默认报错信息 //在ValidationMessages.properties中填写,com.atguigu.common.valid.ListValues.message = 请输入指定的值, //当报错时,默认报错信息为“请输入指定值” Class<?>[] groups() default { 
   }; Class<? extends Payload>[] payload() default { 
   }; int[] values() default { 
   }; } 

2、编写校验类

校验类实现真正的校验功能,initialize表示初始化功能,isValid判断是否校验成功。

讯享网public class ListvaluesConstraintValidator implements ConstraintValidator<ListValues,Integer> { 
    HashSet<Integer> set = new HashSet<Integer>(); public void initialize(ListValues constraintAnnotation) { 
    for (int value : constraintAnnotation.values()) { 
    set.add(value); } } public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) { 
    return set.contains(integer); } } 
小讯
上一篇 2025-01-14 22:25
下一篇 2025-03-12 12:20

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/34358.html