一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入

 

目录

前言

导包

测试

自定义

优化


前言

参数校验在日常开发时是很常用的操作。为了避免null指针、非法数据存入数据库,我们通常要进行参数校验。我们来看一个参数校验的例子:

    @PostMapping("/save")
    public Object save(@RequestBody UserVO userVO) {
        String mobile = userVO.getMobile();

        //手动逐个 参数校验~ 写法
        if (StringUtils.isBlank(mobile)) {
            return R.bulid(ResultEnum.PARAM_FAIL_CODE,"mobile:手机号码不能为空");
        } else if (!Pattern.matches("^[1][3,4,5,6,7,8,9][0-9]{9}$", mobile)) {
            return R.bulid(ResultEnum.PARAM_FAIL_CODE,"mobile:手机号码格式不对");
        }

        //抛出自定义异常等~写法
        if (StringUtils.isBlank(userVO.getUsername())) {
            throw new ParamException(ResultEnum.PARAM_FAIL_CODE, "用户名不能为空");
        }

        // 比如写一个map返回
        if (StringUtils.isBlank(userVO.getSex())) {
            Map result = new HashMap<>(5);
            result.put("code", Constant.PARAM_FAIL_CODE);
            result.put("msg", "性别不能为空");
            return result;
        }
        //.........各种写法 ...
        userService.save(userVO);
        return R.success();
    }

都0202年了,还这么写参数校验?

validation相信大家都听说过,Spring又怎么会放过这个好东西。传统的validation用起来还挺费劲,但是Springboot他来了~轻松几步搞定参数校验。

先上步骤

导包

只需要spring-boot-starter-validation和web即可



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.13.RELEASE
         
    
    com.example
    validation-demo
    0.0.1-SNAPSHOT
    validation-demo
    Demo project for Spring Boot

    
        1.8
    

    

        
            org.springframework.boot
            spring-boot-starter
        
        
        
            org.springframework.boot
            spring-boot-starter-validation
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


写测试DTO

public class TestDTO {

    @NotNull(message = "姓名不能为空")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Max(value = 120, message = "最大值不能大于120")
    @Min(value = 0, message = "最小值不能低于0")
    private Integer age;

    //get set省略
}

这里给你们一份说明 

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第1张图片

 

测试

@RestController
@RequestMapping("/test")
public class TestController {
    /**
     * 测试
     * `@Valid` 表示对这个对象校验
     * `BindingResult` 获取的是校验的结果,这个对象有许多方法获取校验信息,可以自定义返回信息
     * 
     *
     * @param dto
     * @param bindingResult
     * @return
     */
    @PostMapping("/post")
    public Map test(@Validated @RequestBody TestDTO dto, BindingResult bindingResult) {
        Map res = new HashMap<>();
        if (bindingResult.hasErrors()) {
            res.put("status", 400);
            res.put("msg", bindingResult.getFieldError().getDefaultMessage());
            return res;
        } else {
            res.put("status", 200);
            res.put("msg", "ok");
            res.put("data", dto);
            return res;
        }

    }
}

 

好了,结束。测试一下,这里使用IDEA插件RestfulToolkit测试。

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第2张图片

自定义

所有强大的东西在于可以自定义,接下来我们看看如何自定义校验注解。

这里我们用比较常见的标志位校验需求来做示例

1.创建自定义注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Constraint(validatedBy = FlagValidatorClass.class) // 绑定对应校验器
public @interface FlagValidator {
    String[] value() default {};

    String message() default "flag is not found";

    Class[] groups() default {};

    Class[] payload() default {};
}

2.编写对应校验器

/**
 * 标志位校验器
 */
public class FlagValidatorClass implements ConstraintValidator {
    private String[] values;

    /**
     * 初始化
     *
     * @param flagValidator 注解上设置的值
     */
    @Override
    public void initialize(FlagValidator flagValidator) {
        this.values = flagValidator.value();
    }

    /**
     * 校验
     *
     * @param value  被校验的值,即输入
     * @param constraintValidatorContext 校验上下文
     * @return 返回true证明校验通过,false校验失败
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
        boolean isValid = false;
        // 当value为null,校验失败
        if (value == null) {
            return false;
        }
        //遍历校验
        for (int i = 0; i < values.length; i++) {
            if (values[i].equals(String.valueOf(value))) {
                isValid = true;
                break;
            }
        }
        return isValid;
    }
}

这样子就完成了自定义参数校验。当然了,我们可以在isValid方法中添加更加复杂的校验逻辑,如正则匹配等方法,这里就不展开了。

测试一下,先在DTO中添加flag属性,标上注解。

    @NotNull(message = "标识位不能为空")
    @FlagValidator(value = {"0", "1"}, message = "标志位有误")
    private Integer flag;

效果:

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第3张图片

接着我们来看看BindResult有什么内容。

我们输出一下这个对象

   System.out.println(bindingResult);
// 成功时,这没什么好说的
org.springframework.validation.BeanPropertyBindingResult: 0 errors

// 失败时
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'testDTO' on field 'flag': rejected value [12]; 
codes [FlagValidator.testDTO.flag,FlagValidator.flag,FlagValidator.java.lang.Integer,FlagValidator]; 
arguments 
[org.springframework.context.support.DefaultMessageSourceResolvable: codes [testDTO.flag,flag]; 
arguments []; 
default message [flag],[Ljava.lang.String;@1017351]; 
default message [标志位有误]

我们可以看到一些属性,Field、Default message、Rejected value。

我们可以通过他的方法获取对应的值。

获取Error,getFieldError默认返回第一个错误。getAllErrors返回错误列表。

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第4张图片

获取对应的属性。 

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第5张图片

构造我们自己的校验返回内容。

通过Java8的stream方法我们可以简单的获取到报错信息并返回前端;

   Object[] objects =  bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).toArray();

 

一看就懂!三分钟搞定Springboot+validation整合,轻松验证用户输入_第6张图片

优化

每次都要写bindingResult以及判断方法其实也挺烦人的,所以再写一个工具类,constraintViolations是一个Set集合,里面包含了错误的信息,我们可以自定义一个异常抛出,并定义全局异常处理器处理请求返回。

public class ValidatorUtils {
    private static Validator validator;

    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws ApiException 校验不通过,则报ApiException异常
     */
    public static void validateEntity(Object object, Class... groups) {
        Set> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            constraintViolations.forEach(o -> {
                throw new ApiException(400, o.getMessage());
            });
        }
    }
}

 修改TestController,可以看到简洁了许多。效果是一样的,就不演示了。

@RestController
@RequestMapping("/test")
public class TestController {
    /**
     * 测试
     * @param dto
     * @param bindingResult
     * @return
     */
    @PostMapping("/post")
    public Map test(@RequestBody TestDTO dto) {
            ValidatorUtils.validateEntity(dto);

            Map res = new HashMap<>();
        
            res.put("status", 200);
            res.put("msg", "ok");
            res.put("data", dto);
            return res;
        

    }
}

 

这次Springboot+validation整合就讲到这里。

代码比较简单,源码就不奉上来,有需要的可以私信我。

 

 

 


有什么问题可以评论或者私信我,每日在线解(LIAO)疑(SAO)。

我是大誌,一位准备996的卑微码农,觉得好用记得点赞收藏!!!

你可能感兴趣的:(技术干货)