精华内容
下载资源
问答
  • 普通模式(默认是这个模式):会校验完所有的属性,然后返回所有的验证失败信息。 快速失败模式:只要有一个验证失败,则返回。 通常在实际开发中,我们需要配置快速失败模式,快速失败模式的配置方式: package...

    检验模式

    上面例子中一次性返回了所有验证不通过的集合,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。Hibernate Validator有以下两种验证模式:

    1. 普通模式(默认是这个模式):会校验完所有的属性,然后返回所有的验证失败信息。

    2. 快速失败模式:只要有一个验证失败,则返回。

    通常在实际开发中,我们需要配置快速失败模式,快速失败模式的配置方式:

    package com.morris.validator.config;
    
    import org.hibernate.validator.HibernateValidator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
    
    import javax.validation.Validation;
    import javax.validation.Validator;
    import javax.validation.ValidatorFactory;
    
    @Configuration
    public class ValidatorConfig {
    
        @Bean
        public Validator validator() {
            ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                    .configure()
                    // 快速失败模式
                    .failFast(true)
                    .buildValidatorFactory();
            return validatorFactory.getValidator();
        }
    }
    

    也可以如下配置:

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败模式
                //.failFast(true)
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
    

    参数校验

    post参数校验

    post参数校验需要在方法的参数前面加上注解 @Valid,然后就可以在方法参数对应的实体类的字段上面加上@NotNull、@NotEmpty注解进行校验了。

    @PostMapping("add")
    public R add(@Valid @RequestBody User user) {
        return R.ok();
    }
    

    get参数校验

    get参数校验无法使用注解@Valid,需要使用@Validated注解来使得验证生效。

    注入MethodValidationPostProcessor

    在ValidatorConfig注入MethodValidationPostProcessor来开启@Validated注解的校验功能。

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        // 设置validator模式为快速失败返回
        postProcessor.setValidator(validator());
        return postProcessor;
    }
    

    Controller中的配置

    Controller上面添加@Validated
    注解,方法参数上面添加校验注解。

    package com.morris.validator.controller;
    
    import com.morris.validator.vo.R;
    import com.morris.validator.vo.User;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.validation.Valid;
    import javax.validation.constraints.NotBlank;
    
    @RestController
    @RequestMapping("user")
    @Validated
    public class GetController {
    
        @PostMapping("get")
        public R add(@NotBlank(message = "status不能为空") String status) {
            return R.ok();
        }
    
    }
    

    ConstraintViolationException异常处理

    对get参数校验会抛出ConstraintViolationException异常,所以需要在GlobalExceptionHandler中对ConstraintViolationException异常进行处理。

    @ExceptionHandler(value = ConstraintViolationException.class)
    public R ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
        R r = null;
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
        while (iterator.hasNext()) {
            ConstraintViolation<?> cvl = iterator.next();
            r = R.fail(HttpStatus.BAD_REQUEST.value(), cvl.getMessageTemplate());
            break;
        }
        log.error("Custom exception! error={}", ex);
        return r;
    }
    

    测试

    # curl -X POST http://localhost:8080/user/get -H "content-type:application/json"
    {"code":400,"message":"status不能为空","result":null}
    
    # curl -X POST http://localhost:8080/user/get?status= -H "content-type:application/json"
    {"code":400,"message":"status不能为空","result":null}
    
    # curl -X POST http://localhost:8080/user/get?status=xxxx -H "content-type:application/json"
    {"code":200,"message":"OK","result":null}
    

    级联校验

    级联校验需要在校验的属性上面加上@Valid注解。

    public class User {
    ... ...
        @Valid
        private Address address;
    }
    
    @Data
    public class Address {
    
        @NotBlank(message = "province不能为空")
        private String province;
    
        private String city;
    }
    

    测试:

    # curl -X POST http://localhost:8080/user/add -H "content-type:application/json" -d '{"userName":"xx", "age": 10, "address": {"province": "ShangHai"}}'
    {"code":200,"message":"OK","result":null}
    
    # curl -X POST http://localhost:8080/user/add -H "content-type:application/json" -d '{"userName":"xx", "age": 10, "address": {"province": ""}}'
    {"code":400,"message":"province不能为空","result":null}
    

    分组

    Person类字段分组校验如下:

    public interface PersonCreateGroup {
    }
    
    public interface PersonUpdateGroup {
    }
    
    @Data
    public class Person {
    
        @NotNull(message = "id不能为空", groups = PersonUpdateGroup.class)
        private Integer id;
    
        @NotNull(message = "userName不能为空", groups = {PersonCreateGroup.class, PersonUpdateGroup.class})
        @NotBlank(message = "userName不能为空", groups = {PersonCreateGroup.class, PersonUpdateGroup.class})
        private String userName;
    
        @NotNull(message = "age不能为空")
        private Integer age;
    }
    

    Person类中三个分组分别对应的字段如下:

    • PersonUpdateGroup:id、userName

    • PersonCreateGroup:userName

    • Default:age

    PersonController代码如下:

    package com.morris.validator.controller;
    
    import com.morris.validator.vo.*;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.validation.Valid;
    
    @RestController
    @RequestMapping("person")
    @Validated
    public class PersonController {
    
        @PostMapping("add")
        @Validated(PersonCreateGroup.class)
        public R add(@Valid @RequestBody Person person) {
            return R.ok();
        }
    
        @PostMapping("update")
        @Validated(PersonUpdateGroup.class)
        public R update(@Valid @RequestBody Person person) {
            return R.ok();
        }
    
        @PostMapping("add2")
        public R add2(@Valid @RequestBody Person person) {
            return R.ok();
        }
    
    }
    

    下面分别对PersonController中的各个接口进行测试:

    add:

    # curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"xxx", "age": 10}'
    {"code":200,"message":"OK","result":null}
    
    # curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"xxx"}'
    {"code":400,"message":"age不能为空","result":null}
    
    # curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"age":10}'
    {"code":400,"message":"userName不能为空","result":null}
    

    update:

    # curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1, "userName":"xxx", "age": 10}'
    {"code":200,"message":"OK","result":null}
    
    # curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"userName":"xxx", "age": 10}'
    {"code":400,"message":"id不能为空","result":null}
    
    # curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1,  "age": 10}'
    {"code":400,"message":"userName不能为空","result":null}
    
    # curl -X POST http://localhost:8080/person/update -H "content-type:application/json" -d '{"id":1, "userName":"xxx"}'
    {"code":400,"message":"age不能为空","result":null}
    

    add2:

    # curl -X POST http://localhost:8080/person/add2 -H "content-type:application/json" -d '{"age": 10}'
    {"code":200,"message":"OK","result":null}
    
    # curl -X POST http://localhost:8080/person/add2 -H "content-type:application/json" -d '{}'
    {"code":400,"message":"age不能为空","result":null}
    

    总结:

    • 校验时指定了group,就会校验指定group和默认group

    • 校验时没指定group,只会校验默认group

    校验时可以指定多个分组,并为这些分组指定顺序:

    @GroupSequence({OneGroup.class, TwoGroup.class, ThreeGroup.class})
    public interface PersonOrder {
    }
    

    自定义校验器

    一般情况,内置的校验器可以解决很多问题。但也有无法满足情况的时候,此时,我们可以实现validator的接口,自定义自己需要的验证器。

    如下所示,实现了一个身份证规则验证器:

    package com.morris.validator.validator;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class IdCardValidator implements ConstraintValidator<IdCard, String> {
    
        /**
         * 身份证规则校验正则表达式
         */
        private String reg = "^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9]|X)$";
        private Pattern pt = Pattern.compile(reg);
    
        @Override
        public void initialize(IdCard idCard) {
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext arg1) {
            if (value == null) {
                return true;
            }
            Matcher m = pt.matcher(value);
            if (m.find()) {
                return true;
            }
            return false;
        }
    
    }
    

    自定义注解:

    package com.morris.validator.validator;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy= IdCardValidator.class)
    public @interface IdCard {
    
        String message() default "身份证号码格式不对";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    

    使用如下:

    public class Person {
    
    ... ...
        @IdCard(message = "身份证号码格式不对")
        private String idCardNo;
    }
    

    测试如下:

    # curl -X POST http://localhost:8080/person/add -H "content-type:application/json" -d '{"userName":"morris","age":10, "idCardNo":"33"}'
    {"code":400,"message":"身份证号码格式不对","result":null}
    

    参考文档

    展开全文
  • 主要介绍了spring @Validated 注解开发中使用group分组校验的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • JSR303分组校验

    2021-08-29 23:26:09
    JSR303分组校验属于后端校验的一种很方便的方式 步骤: 1.新建两个接口,用来代表哪两个组,比如,AddGroup,UpdateGroup代表添加分组和修改分组 //添加分组 package com.jc.common.valid; public interface ...

    JSR303分组校验属于后端校验的一种很方便的方式
    步骤:

    1.新建两个接口,用来代表哪两个组,比如,AddGroup,UpdateGroup代表添加分组和修改分组

    //添加分组
    package com.jc.common.valid;
    
    public interface AddGroup {
    }
    
    //修改分组
    package com.jc.common.valid;
    
    public interface UpdateGroup {
    }
    
    
    

    2.给需要验证的实体类的字段上添加对应的校验注解,并且指定其分组

    package com.jc.gulimall.product.entity;
    
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import com.jc.common.valid.AddGroup;
    import com.jc.common.valid.UpdateGroup;
    import lombok.Data;
    import org.hibernate.validator.constraints.URL;
    
    import javax.validation.constraints.*;
    
    /**
     * 品牌
     * 
     * @author wjc
     * @email 1678912421@gmail.com
     * @date 2021-07-16 21:22:32
     */
    @Data
    @TableName("pms_brand")
    public class BrandEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
    
    	/**
    	 * 品牌id
    	 */
    	@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
    	@Null(message = "添加不能添加品牌id",groups = {AddGroup.class})
    	@TableId
    	private Long brandId;
    	/**
    	 * 品牌名
    	 */
    
    
    	@NotBlank(message = "品牌名不能为空!")
    	private String name;
    	/**
    	 * 品牌logo地址
    	 */
    	@URL(message = "logo地址必须是一个合法的URL!",groups = {AddGroup.class,UpdateGroup.class})
    	@NotNull(message = "logo不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	private String logo;
    	/**
    	 * 介绍
    	 */
    	private String descript;
    	/**
    	 * 显示状态[0-不显示;1-显示]
    	 */
    	private Integer showStatus;
    	/**
    	 * 检索首字母
    	 */
    	@NotNull(message = "首字母不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字符必须是一个字母",groups = {AddGroup.class,UpdateGroup.class})
    	private String firstLetter;
    	/**
    	 * 排序
    	 */
    	@NotNull(message = "排序字段不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	@Min(value = 0,message = "排序必须大于等于0",groups = {AddGroup.class,UpdateGroup.class})
    	private Integer sort;
    
    }
    
    

    上边的logo,firstLetter,sort字段我都添加了两个分组,也就是说在修改和添加的情况下都会校验

    3.在url对应的方法参数中需要校验的实体类前加上@Validated(AddGroup.class)注解,在需要校验的实体类中标注AddGroup分组的字段进行校验,@Validated注解后边不指定分组类别,则默认是对不标注分组的字段进行校验

    	//对BrandEntity 字段中指定UpdaGroup分组的字段就行校验 
        @RequestMapping("/update")
        public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
    		brandService.updateByid(brand);
    
            return R.ok();
        }
    
        @RequestMapping("/save")
        public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand/*, BindingResult bindingResult*/){
    
    //    统一异常处理
    //            if (bindingResult.hasErrors()){
    //                Map<String,String> map = new HashMap<>();
    //                bindingResult.getFieldErrors().stream().forEach((item)->{
    //                    String defaultMessage = item.getDefaultMessage();
    //                    String field = item.getField();
    //                    map.put(field,defaultMessage);
    //                });
    //                return R.error(400,"提交的数据不合法").put("data",map);
    //            }
    
                brandService.save(brand);
                return R.ok();
        }
    

    实践:
    在save请求中,对实体类标注了AddGroup分组,则对实体类中所有标注AddGroup的分组就行校验,
    对应上边的也就是,

    	@Null(message = "添加不能添加品牌id",groups = {AddGroup.class})
    	private Long brandId;
    	@NotNull(message = "首字母不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字符必须是一个字母",groups = {AddGroup.class,UpdateGroup.class})
    	private String firstLetter;
    	/**
    	 * 排序
    	 */
    	@NotNull(message = "排序字段不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	@Min(value = 0,message = "排序必须大于等于0",groups = {AddGroup.class,UpdateGroup.class})
    	private Integer sort;
    	@URL(message = "logo地址必须是一个合法的URL!",groups = {AddGroup.class,UpdateGroup.class})
    	@NotNull(message = "logo不能为空!",groups = {AddGroup.class,UpdateGroup.class})
    	private String logo;
    

    在这里插入图片描述
    下面我们再看没有标注任何分组的字段,也就是name字段

    	@NotBlank(message = "品牌名不能为空!")
    	private String name;
    

    我们将BrandController中的update方法中分组去掉原来是@Validated(updateGroup.class)

        @RequestMapping("/update")
        public R update(@Validated @RequestBody BrandEntity brand){
    		brandService.updateByid(brand);
    
            return R.ok();
        }
    
    

    由于除了name之外的字段都标注了分组,所以name之外的字段是不进行校验的,只有没有标注分组的name字段才进行校验。
    在这里插入图片描述

    总结:
    在需要校验的实体类中,哪个字段标注了哪个分组,就在方法参数注解中指定校验分组的情况下进行校验。
    字段没有标注分组的情况下,就在方法参数注解中没指定校验分组的情况下进行校验。

    展开全文
  • 在日常的开发中,**参数校验**是非常重要的一个环节,严格参数校验会减少很多出bug的概率,增加接口的安全性。在此之前写过一篇[SpringBoot统一参数校验](https://juejin.im/post/6844904081660575752)主要介绍了...

    前言

      在日常的开发中,参数校验是非常重要的一个环节,严格参数校验会减少很多出bug的概率,增加接口的安全性。在此之前写过一篇SpringBoot统一参数校验主要介绍了一些简单的校验方法。而这篇则是介绍一些进阶的校验方式。比如说:在某个接口编写的过程中肯定会遇到,当xxType值为A,paramA值必传。xxType值为B,paramB值必须传。对于这样的,通常的做法就是在controller加上各种if判断。显然这样的代码是不够优雅的,而分组校验及自定义参数校验,就是来解决这个问题的。

    PathVariable参数校验

      Restful的接口,在现在来讲应该是比较常见的了,常用的地址栏的参数,我们都是这样校验的。

    /**
     * 获取电话号码信息
     */
    @GetMapping("/phoneInfo/{phone}")
    public ResultVo phoneInfo(@PathVariable("phone") String phone){
        // 验证电话号码是否有效
        String pattern = "^[1][3,4,5,7,8][0-9]{9}$";
        boolean isValid =  Pattern.matches(pattern, phone);
        if(isValid){
            // 执行相应逻辑
            return ResultVoUtil.success(phone);
        } else {
            // 返回错误信息
            return ResultVoUtil.error("手机号码无效");
        }
    }
    

    很显然上面的代码不够优雅,所以我们可以在参数后面,添加对应的正则表达式phone:正则表达式来进行验证。这样就省去了在controller编写校验代码了。

    /**
     * 获取电话号码信息
     */
    @GetMapping("/phoneInfo/{phone:^[1][3,4,5,7,8][0-9]{9}$}")
    public ResultVo phoneInfo(@PathVariable("phone") String phone){
        return ResultVoUtil.success(phone);
    }
    

    虽然这样处理后代码更精简了。但是如果传入的手机号码,不符合规则会直接返回404。而不是提示手机号码错误。错误信息如下:

    自定义校验注解

      我们以校验手机号码为例,虽然validation提供了@Pattern这个注解来使用正则表达式进行校验。如果被使用在多处,一旦正则表达式发生更改,则需要一个一个的进行修改。很显然为了避免做这样的无用功,自定义校验注解就是你的好帮手。

    @Data
    public class PhoneForm {
    
        /**
         * 电话号码
         */
        @Pattern(regexp = "^[1][3,4,5,7,8][0-9]{9}$" , message = "电话号码有误")
        private String phone;
    
    }
    

      要实现一个自定义校验注解,主要是有两步。一是注解本身,二是校验逻辑实现类

    PhoneVerify 校验注解

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = PhoneValidator.class)
    public @interface Phone {
     
        String message() default "手机号码格式有误";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
    }
    

    PhoneValidator 校验实现类

    public class PhoneValidator implements ConstraintValidator<Phone, Object> {
    
        @Override
        public boolean isValid(Object telephone, ConstraintValidatorContext constraintValidatorContext) {
            String pattern = "^1[3|4|5|7|8]\\d{9}$";
            return Pattern.matches(pattern, telephone.toString());
        }
    }
    

    CustomForm 表单数据

    @Data
    public class CustomForm {
    
        /**
         * 电话号码
         */
        @Phone
        private String phone;
    
    }
    

    测试接口

    @PostMapping("/customTest")
    public ResultVo customTest(@RequestBody @Validated CustomForm form){
    	return ResultVoUtil.success(form.getPhone());
    }
    

    注解的含义

    @Target({ElementType.FIELD})

      注解是指定当前自定义注解可以使用在哪些地方,这里仅仅让他可以使用属性上。但还可以使用在更多的地方,比如说方法上、构造器上等等。

    • TYPE - 类,接口(包括注解类型)或枚举
    • FIELD - 字段(包括枚举常量)
    • METHOD - 方法
    • PARAMETER - 参数
    • CONSTRUCTOR - 构造函数
    • LOCAL_VARIABLE - 局部变量
    • ANNOTATION_TYPE -注解类型
    • PACKAGE - 包
    • TYPE_PARAMETER - 类型参数
    • TYPE_USE - 使用类型
    @Retention(RetentionPolicy.RUNTIME)

      指定当前注解保留到运行时。保留策略有下面三种:

    • SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃。
    • CLASS - 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
    • RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
    @Constraint(validatedBy = PhoneValidator.class)

      指定了当前注解使用哪个校验类来进行校验。

    分组校验

    UserForm

    @Data
    public class UserForm {
    
        /**
         * id
         */
        @Null(message = "新增时id必须为空", groups = {Insert.class})
        @NotNull(message = "更新时id不能为空", groups = {Update.class})
        private String id;
    
        /**
         * 类型
         */
        @NotEmpty(message = "姓名不能为空" , groups = {Insert.class})
        private String name;
    
        /**
         * 年龄
         */
        @NotEmpty(message = "年龄不能为空" , groups = {Insert.class})
        private String age;
        
    }
    

    Insert分组

    public interface Insert {
    }
    

    Update分组

    public interface Update {
    }
    

    测试接口

    /**
     * 添加用户
     */
    @PostMapping("/addUser")
    public ResultVo addUser(@RequestBody @Validated({Insert.class}) UserForm form){
      	// 选择对应的分组进行校验
        return ResultVoUtil.success(form);
    }
    
    /**
     * 更新用户
     */
    @PostMapping("/updateUser")
    public ResultVo updateUser(@RequestBody @Validated({Update.class}) UserForm form){
        // 选择对应的分组进行校验
        return ResultVoUtil.success(form);
    }
    

    测试结果

    添加测试

    更新测试

    顺序校验@GroupSequence

      在@GroupSequence内可以指定,分组校验的顺序。比如说@GroupSequence({Insert.class, Update.class, UserForm.class})先执行Insert校验,然后执行Update校验。如果Insert分组,校验失败了,则不会进行Update分组的校验。

    @Data
    @GroupSequence({Insert.class, Update.class, UserForm.class})
    public class UserForm {
    
        /**
         * id
         */
        @Null(message = "新增时id必须为空", groups = {Insert.class})
        @NotNull(message = "更新时id不能为空", groups = {Update.class})
        private String id;
    
        /**
         * 类型
         */
        @NotEmpty(message = "姓名不能为空" , groups = {Insert.class})
        private String name;
    
        /**
         * 年龄
         */
        @NotEmpty(message = "年龄不能为空" , groups = {Insert.class})
        private String age;
    
    }
    
    测试接口
    /**
    * 编辑用户
    */
    @PostMapping("/editUser")
    public ResultVo editUser(@RequestBody @Validated UserForm form){
    	return ResultVoUtil.success(form);
    }
    
    测试结果

      哈哈哈,测试结果其实是个死循环,不管你咋输入都会报错,小伙伴可以尝试一下哦。上面的例子只是个演示,在实际中还是别这样做了,需要根据具体逻辑进行校验。

    自定义分组校验

      对于之前提到了当xxType值为A,paramA值必传。xxType值为B,paramB值必须传这样的场景。单独使用分组校验和分组序列是无法实现的。需要使用@GroupSequenceProvider才行。

    自定义分组表单

    @Data
    @GroupSequenceProvider(value = CustomSequenceProvider.class)
    public class CustomGroupForm {
    
        /**
         * 类型
         */
        @Pattern(regexp = "[A|B]" , message = "类型不必须为 A|B")
        private String type;
    
        /**
         * 参数A
         */
        @NotEmpty(message = "参数A不能为空" , groups = {WhenTypeIsA.class})
        private String paramA;
    
        /**
         * 参数B
         */
        @NotEmpty(message = "参数B不能为空", groups = {WhenTypeIsB.class})
        private String paramB;
    
        /**
         * 分组A
         */
        public interface WhenTypeIsA {
    
        }
    
        /**
         * 分组B
         */
        public interface WhenTypeIsB {
    
        }
    
    }
    

    CustomSequenceProvider

    public class CustomSequenceProvider implements DefaultGroupSequenceProvider<CustomGroupForm> {
    
        @Override
        public List<Class<?>> getValidationGroups(CustomGroupForm form) {
            List<Class<?>> defaultGroupSequence = new ArrayList<>();
    
            defaultGroupSequence.add(CustomGroupForm.class);
    
            if (form != null && "A".equals(form.getType())) {
                defaultGroupSequence.add(CustomGroupForm.WhenTypeIsA.class);
            }
    
            if (form != null && "B".equals(form.getType())) {
                defaultGroupSequence.add(CustomGroupForm.WhenTypeIsB.class);
            }
    
            return defaultGroupSequence;
        }
    }
    

    测试接口

    /**
     * 自定义分组
     */
    @PostMapping("/customGroup")
    public ResultVo customGroup(@RequestBody @Validated CustomGroupForm form){
        return ResultVoUtil.success(form);
    }
    

    测试结果

    Type类型为A

    Type类型为B

    小结一下

      GroupSequence注解是一个标准的Bean认证注解。正如之前,它能够让你静态的重新定义一个类的,默认校验组顺序。然而GroupSequenceProvider它能够让你动态的定义一个校验组的顺序。

    注意的一个点

    SpringBoot 2.3.x 移除了validation依赖需要手动引入依赖。

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    

    总结

      个人的一些小经验,参数的非空判断,这个应该是校验的第一步了,除了非空校验,我们还需要做到下面这几点:

    • 普通参数 - 需要限定字段的长度。如果会将数据存入数据库,长度以数据库为准,反之根据业务确定。
    • 类型参数 - 最好使用正则对可能出现的类型做到严格校验。比如type的值是【0|1|2】这样的。
    • 列表(list)参数 - 不仅需要对list内的参数是否合格进行校验,还需要对list的size进行限制。比如说 100。
    • 日期,邮件,金额,URL这类参数都需要使用对于的正则进行校验。
    • 参数真实性 - 这个主要针对于 各种Id 比如说 userIdmerchantId,对于这样的参数,都需要进行真实性校验,判断系统内是有含有,并且对应的状态是否正常。

      参数校验越严格越好,严格的校验规则不仅能减少接口出错的概率,同时还能避免出现脏数据,从而来保证系统的安全性和稳定性。

    错误的提醒信息需要友好一点哦,防止等下被前端大哥吐槽哦。

    上期回顾

    结尾

      如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。

      我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!

    展开全文
  • 文章目录概述普通校验 概述 Spring Validation 验证框架与 javax 都提供了对数据的校验功能,一个是 @Validated(JSR-303变种),一个是 @Valid(标准JSR-303)。 普通校验 添加validation依赖: <dependency...


    概述

    Spring Validation 验证框架与 javax 都提供了对数据的校验功能,一个是 @Validated(JSR-303变种),一个是 @Valid(标准JSR-303)。


    普通校验

    添加validation依赖:

    		<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
            <!-- 回顾下lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
            </dependency>
    

    通过 LocalValidatorFactoryBean 类可以看到,校验的提示信息可以写在 ValidationMessages.properties 文件中:

    	user.name.notnull=用户名不能为空
    	user.password.notnull=密码不能为空
    	user.email.notnull=邮箱不能为空
    	user.email.pattern=邮箱格式不正确
    

    编写User实体类:

    		@Data
    		public class User {
    		    @NotNull(message = "{user.name.notnull}")
    		    private String name;
    		
    		    @NotNull(message = "{user.password.notnull}")
    		    private String password;
    		
    		    @Email(message = "{user.email.pattern}")
    		    @NotNull(message = "{user.email.notnull}")
    		    private String email;
    		}
    

    这里只是测试,就直接写Controller接口,使用 @Validated 注解:

    		@RestController
    		@RequestMapping("/valid")
    		public class DemoController {
    		
    		    @PostMapping("/addUser")
    		    public String addUser(@Validated @RequestBody User user){
    		        String res = "用户名:"+user.getName()+",密码:"+user.getPassword()+",邮箱:"+user.getEmail();
    		        return res;
    		    }
    		}
    

    如果校验的结果不对,默认会返回400,这里我们通过ResponseEntityExceptionHandler自定义异常处理类,使得我们的接口能返回错误信息:

    		@RestControllerAdvice
    		public class ValidatorHandlerAdvice extends ResponseEntityExceptionHandler {
    		    @Override
    		    protected ResponseEntity<Object> handleMethodArgumentNotValid(
    		            MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status,
    		            WebRequest request) {
    		        List<String> errors = new ArrayList<>();
    		        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
    		        for(ObjectError error : allErrors){
    		            errors.add(error.getDefaultMessage());
    		        }
    		        return new ResponseEntity(errors, status);
    		    }
    		}
    

    使用 Postman 进行测试:

    image-20200725184058279


    分组校验

    某一个实体中定义了很多的校验规则,但有时候我们并不需要这么多的校验规则,这个时候就可以使用分组校验。

    创建两个分组接口:

    public interface UserValidationGroup1 {
    }
    public interface UserValidationGroup2 {
    }
    

    修改User实体类:

            @Data
            public class User {
                @NotNull(message = "{user.name.notnull}",groups = {UserValidationGroup1.class, UserValidationGroup2.class})
                private String name;
    
                @NotNull(message = "{user.password.notnull},groups = {UserValidationGroup1.class, UserValidationGroup2.class}")
                private String password;
    
                @Email(message = "{user.email.pattern},groups = UserValidationGroup1.class")
        		@NotNull(message = "{user.email.notnull},groups = UserValidationGroup2.class")
                private String email;
            }
    

    接口指定分组:

            @RestController
            @RequestMapping("/valid")
            public class DemoController {
                @PostMapping("/addUser")
                public String addUser(@Validated(UserValidationGroup2.class) @RequestBody User user){
                    String res = "用户名:"+user.getName()+",密码:"+user.getPassword()+",邮箱:"+user.getEmail();
                    return res;
                }
            }
    

    @Validated指定UserValidationGroup2分组,不会校验邮箱格式:

    image-20200725190653595


    校验注解

    注解作用
    @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=)被注释的元素必须符合指定的正则表达式
    @NotBlank(message =)验证字符串非null,且长度必须大于0
    @Email被注释的元素必须是电子邮箱地址
    @Length(min=,max=)被注释的字符串的大小必须在指定的范围内
    @NotEmpty被注释的字符串的必须非空
    @Range(min=,max=,message=)被注释的元素必须在合适的范围内

    嵌套校验

    有诸如一下形式:

    class Person{
        @NotNull
        private String name;
        ...
    }
    class Room{
        @NotNull
        @Valid	//只能使用@Valid
        List<Person> personList;
    }
    

    这样才能对 personList 中的 Person 进行校验。


    @Validated和@Valid区别

    @Validated 或者@Valid 在基本验证功能上没有太多区别,需要注意的是:

    • 分组:@Validated 有分组功能,而 @Valid 还没有。

    • 作用地方:@Validated 可以用在类型、方法和方法参数,不能用在成员属性上;而 @Valid 可以用在方法、构造函数、方法参数和成员属性上。

    展开全文
  • 普通校验 普通检验是基础用法,非常容易,首先需要用户在SpringBootWeb项目中添加数据校验相关的依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring...
  • 我们经常需要对接口的请求参数进行校验,在没有使用validation之前都是用if去做校验的,但这样通常需要写一长串的if语句,使代码看起来非常臃肿,即使将这些校验封装到一个方法或是类中,然后调用,但本质上并没有...
  • java 分组校验

    2021-07-29 16:33:18
    分组校验 添加分组接口(空接口就可以,不用做任何实现) // 添加的分组校验接口 public interface AddGroup { } // 修改的分组校验接口 public interface UpdateGroup { } 在要验证的Bean上添加校验分组 , 标注什么...
  • 通用校验 Controller... public Object test(@RequestBody @Validated ReqBO reqBO, BindingResult bindingResult) { if (bindingResult.hasErrors()) { List<ObjectError> allErrors = bindingResult....
  • 两个表单对应同一个User类的数据成员,要想表单数据校验必须使用分组校验才能实现,不然登录和注册不能使用同一个User类 创建两个空的接口 public interface ValidateLoginForm { } public interface...
  • hibernate validate分组校验

    千次阅读 2019-02-17 15:21:17
    例如用户(User)在注册时可能对于是否邮件填写敏感,而用户添加课程学习列表时也只对用户当前添加课程关心,这样我们之前统一的校验就不起作用了,而hibernate提供的分组功能就能帮助我们实现.直接上代码。 ...
  • SpringBoot系列17-validation分组校验+自定义参数校验器 原文链接: https://www.lskyf.com/post/212 加入依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId>...
  • 校验注解 (1)给Bean的属性添加注解:javax.validation.constraints.xxx (2)在Controller方法参数上加注解:@Valid @TableName("brand") public class BrandEntity implements Serializable { private static ...
  • 前端校验后,为什么需要后端校验 在前面完成数据参数绑定到Controller时,我们可以在@RequestParam注解中做简单的空校验,就是设置required属性为true,以此来指定Controller方法中形参是否必须传入。数据校验是很...
  • 3、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果 4、分组校验(多场景的复杂校验) 1)、 @NotBlank(message = “品牌名必须提交”,groups = {AddGroup.class,UpdateGroup.class}) 给校验注解标注...
  • 相信只要是写过项目,或者说是自己写一些练手的小系统的都写过表单校验。之前我自己在练手系统中,写表单校验通常都是在前端进行一些正则校验或者是其他的可行性校验,比如是否为空,手机号,身份证号之类的。但是...
  • springmvc分组校验

    2019-05-06 23:29:25
    分组教研 需求 ...定义多个校验分组(java接口),分组中有哪些定义规则 每个controller方法使用不同的校验规则 一、写多个分组接口 二、将需要的校验规则加入到不同的分组 三、controller中调...
  • 分组校验 @Max注解指明这个数据属于哪个分组它才做验证;那么,一个数据传过来,这个数据怎么知道自己是哪个组? @NotNull @Min(0) //最小值>=0 @Max(10) //最大值<=10 @Max(value=0, message="未发行的...
  • 分组参数验证 接口类 实体类 控制类 测试 @Valid和@Validated 区别 简述 @Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。 @...
  • 严谨的接口是必须要有参数校验的,回避掉一些不合理的请求。但是校验逻辑和正常业务逻辑掺杂在一起,固然能完成校验需求,但是在实现方式上却不那么优雅。比如现在有一个user注解接口: 1.直男癌一般的校验 @...
  • @Validated 分组校验

    千次阅读 2019-05-14 21:12:38
    @Validated分组校验 需要校验的对象定义 @Data public class ValidateDemo { @NotBlank(groups = UpdateGroup.class, message = "The id can not be empty") private String id; @NotBlank(groups = {SaveGroup....
  • Validation校验----数据校验和分组校验

    千次阅读 2018-10-26 17:26:36
    Spring MVC有多种校验机制,有Bean Validation及Spring Validator接口校验。在Spring4.0之后,支持Bean Validation1.0和Bean Validation1.1校验,可也以单独集成Hibernate的validation校验框架,若要使用Bean ...
  • 此时的Validated校验就会出现冲突,为了解决这种冲突,此时则对Validated进行分组校验。 实体类: package org.sang.model.entity; import org.hibernate.validator.constraints.Length; import javax....
  • //指定分组校验该参数 @NotBlank(groups = {UserInsertCheck.class},message = "id不能为空!") private String id; //两个分组同时校验该参数 @NotBlank(groups = {UserInsertCheck.class,UserUpdateCheck.class},...
  • springboot 分组校验和顺序校验

    千次阅读 2020-04-14 00:18:02
    } } 分组校验 只校验loginName属性 @RequestMapping("/login") @ResponseBody public ModelAndView login(HttpServletRequest request,@Validated({LoginReg.class}) UserInfoVO info,BindingResult br) { ...
  • SpringBoot @Validated注解实现参数分组校验 在前后端分离开发的时候我们需要用到参数校验,前端需要进行参数校验,后端接口同样的也需要,以防传入不合法的数据。 首先还是先导包,导入pom文件。 <dependency&...
  • group指定分组,AddGroup和UpdateGroup均为空接口 @Data @TableName("pms_brand") public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 品牌id */ ...
  • 目录 一、需求 二、实现方式 三、实例 1增加配置文件使用快速失败模式 2创建测试实体类 3创建congtroller ...StringBoot开发POST接口使用HibernateValidator校验@RequestBody注解的参数时,需要对...
  • 利用好javax.validation.groups.Default.class这个默认分组 一、建立不同的分组 注意建立的是接口,继承Default AddGroup public interface AddGroup extends Default { } UpdateGroup public interface ...
  • hibernate-validator分组校验

    千次阅读 2020-01-03 09:18:27
    根据groups来指定需要校验的字段 class Person{ @NotNull(message = "id不能为空!",groups = {Update.class}) Integer id; @NotBlank(message = "姓名不能为空",groups = {Add.class,Update.class}) String ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,605
精华内容 23,042
关键字:

分组校验