Spring Boot整合Validation进行参数校验

一、Validator简介

Bean Validation是Java中的一项标准,它通过一些注解表达了对实体的限制规则。通过提出了一些API和扩展性的规范,这个规范是没有提供具体实现的,希望能够Constrain once, validate everywhere。现在它已经发展到了2.0,兼容Java8。

hibernate validation实现了Bean Validation标准,里面还增加了一些注解,在程序中引入它我们就可以直接使用。

Spring MVC也支持Bean Validation,它对hibernate validation进行了二次封装,添加了自动校验,并将校验信息封装进了特定的BindingResult类中,在SpringBoot中我们可以添加implementation('org.springframework.boot:spring-boot-starter-validation')引入这个库,实现对bean的校验功能。

 

 

二、整合Bean Validation

1. 配置与依赖

导入pom依赖



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

 

参数校验失败后,会抛出异常,我们配置全局异常进行拦截,自定义返回信息

package com.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理
 *
 * @Author: hanYong
 * @CreateTime: 2019-09-19
 */

@RestControllerAdvice
@Slf4j
public class HandleException {

    /**
     * 处理请求参数格式错误
     *
     * @param e 实体中校验失败后抛出的异常
     * @return Object 同步返回500,异步返回JSON
     */
    @ExceptionHandler(BindException.class)
    public Object handleBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String defaultMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
        return result(defaultMessage);
    }

    /**
     * 处理请求参数格式错误
     *
     * @param e @RequestBody上validate失败后抛出的异常
     * @return Object 同步返回500,异步返回JSON
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        return result(fieldError.getDefaultMessage());
    }

    /**
     * 处理请求参数格式错误
     *
     * @param e @RequestParam上validate失败后抛出的此异常
     * @return Object 同步返回500,异步返回JSON
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Object handleConstraintViolationException(ConstraintViolationException e) {
        ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
        return result(constraintViolation.getMessage());
    }

    private Map result(String errorMsg) {
        Map result = new HashMap<>();
        result.put("code", "401");
        result.put("msg", errorMsg);
        return result;
    }

}

 

2. 测试一:对方法形参进行校验

@Validated
@Controller
public class IndexController {

    /**
     * 提交登陆信息
     *
     * @param username 用户名
     * @param password 密码
     * @param captcha  验证码
     * @return 结果
     */
    @PostMapping("/login1")
    @ResponseBody
    public Map submitLogin1(String username, @NotNull(message = "密码不能为空")String password, String captcha) {
        Map resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "登录成功");
        resultMap.put("username", username);
        resultMap.put("password", password);
        resultMap.put("captcha", captcha);
        return resultMap;
    }
}

 

Spring Boot整合Validation进行参数校验_第1张图片

 

3. 测试二:对实体类进行校验

@Data
public class LoginUser {

    @NotNull(message = "用户名不能为空")
    private String username;

    private String password;

    private String captcha;
}

对实体进行校验的时候需要加上@Validated

    /**
     * 提交登陆信息
     *
     * @return 结果
     */
    @PostMapping("/login3")
    @ResponseBody
    public Map submitLogin3(@Validated LoginUser loginUser) {
        Map resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "登录成功");
        resultMap.put("username", loginUser.getUsername());
        resultMap.put("password", loginUser.getPassword());
        resultMap.put("captcha", loginUser.getCaptcha());
        return resultMap;
    }

Spring Boot整合Validation进行参数校验_第2张图片

 

 

三、常用注解介绍

1. validator内置注解

注解 详细信息
@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(value) 被注释的元素必须符合指定的正则表达式

 

2. Hibernate Validator 附加的 constraint

注解 详细信息
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内
@NotBlank 验证字符串非null,且长度必须大于0

 

3. 注意事项

  • @NotNull 适用于任何类型被注解的元素必须不能与NULL

  • @NotEmpty 适用于String Map或者数组不能为Null且长度必须大于0

  • @NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0

 

 

四、自定义注解

自定义一个equals注解

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EqualsValidator.class)
public @interface Equals {

    /**
     * 校验值
     */
    String val() default "";

    /**
     * 校验失败的信息
     */
    String message() default "";

    /**
     * 分组类别
     */
    Class[] groups() default {};

    /**
     * 约束条件的严重级别
     */
    Class[] payload() default {};
}

 

实现ConstraintValidator,添加校验规则

import org.springframework.util.ObjectUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;

public class EqualsValidator implements ConstraintValidator {

    private String value2;

    /**
     * Initializes the validator in preparation for
     * {@link #isValid(String, ConstraintValidatorContext)} calls.
     * The constraint annotation for a given constraint declaration
     * is passed.
     * 

* This method is guaranteed to be called before any use of this instance for * validation. *

* The default implementation is a no-op. * * @param parameters annotation instance for a given constraint declaration */ @Override public void initialize(Equals parameters) { this.value2 = parameters.val(); } /** * Implements the validation logic. * The state of {@code value} must not be altered. *

* This method can be accessed concurrently, thread-safety must be ensured * by the implementation. * * @param value object to validate * @param context context in which the constraint is evaluated * @return {@code false} if {@code value} does not pass the constraint */ @Override public boolean isValid(String value, ConstraintValidatorContext context) { return Objects.equals(value, value2); } }

 

如果需要设置约束条件的严重级别,可以在创建下面类

/**
 * Severity
 * 约束条件的严重级别
 *
 * @Author: hanYong
 * @CreateTime: 2020-05-20
 */
public class Severity {

    public static class Warn implements Payload {
    }

    public static class Error implements Payload {
    }
}

 

进行测试

    /**
     * 提交登陆信息
     *
     * @param username 用户名
     * @param password 密码
     * @param captcha  验证码
     * @return 结果
     */
    @PostMapping("/login1")
    @ResponseBody
    public Map submitLogin1(@Equals(val = "zhangsan", message = "账号错误")String username, String password, String captcha) {
        Map resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "登录成功");
        resultMap.put("username", username);
        resultMap.put("password", password);
        resultMap.put("captcha", captcha);
        return resultMap;
    }

 

Spring Boot整合Validation进行参数校验_第3张图片

 

五、分组校验

在实际开发中,会遇到同一个实体,多个接口使用,每个接口的校验规则也不一样,或者校验顺序不一样的时候,我们就可以使用分组校验

 

创建一个分组类

/**
 * Group
 *
 * @Author: hanYong
 * @CreateTime: 2020-05-19
 */
public interface Group {

    @GroupSequence({
            Create.One.class,
            Create.Two.class,
            Create.Three.class,
    })
    interface Create {
        interface One {
        }

        interface Two {
        }

        interface Three {
        }
    }

    @GroupSequence({
            Modify.One.class,
            Modify.Two.class,
            Modify.Three.class,
    })
    interface Modify {
        interface One {
        }

        interface Two {
        }

        interface Three {
        }

    }
}

 

按照顺序进行分组校验,如果有一个校验不通过,就直接返回校验结果,不在进行后面的校验

新增的时候校验用户名和密码

修改的时候校验用户id、用户名、密码

@Data
public class SysUser {
    @NotNull(message = "用户id不能为空",
            groups = {
                Group.Modify.class, Group.Modify.One.class
            }
    )
    private Integer id;

    @NotNull(message = "用户名不能为空",
            groups = {
                    Group.Create.class, Group.Create.One.class,
                    Group.Modify.class, Group.Modify.Two.class
            }
    )
    private String username;

    @NotNull(message = "用户密码不能为空",
            groups = {
                    Group.Create.class, Group.Create.Two.class,
                    Group.Modify.class, Group.Modify.Three.class
            }
    )
    private String password;
}

 

编写测试接口

    /**
     * 新增用户
     *
     * @param sysUser 用户数据
     * @return 结果
     */
    @PostMapping("/insert")
    @ResponseBody
    public Map insert(@Validated(Group.Create.class) SysUser sysUser) {
        Map resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "新增成功");
        resultMap.put("modifyRoleSet", sysUser.toString());
        return resultMap;
    }

    /**
     * 修改用户
     *
     * @param sysUser 用户数据
     * @return 结果
     */
    @PostMapping("/modify")
    @ResponseBody
    public Map modify(@Validated(Group.Modify.class) SysUser sysUser) {
        Map resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "修改成功");
        resultMap.put("modifyRoleSet", sysUser.toString());
        return resultMap;
    }

Spring Boot整合Validation进行参数校验_第4张图片

Spring Boot整合Validation进行参数校验_第5张图片

 

六、手动获取校验结果

之前我们都是在Controller进行参数校验的,实际开发中,我们在其它地方也会涉及到校验,下面是手动调用校验方法,获取校验结果的示例

新增一个Service类

import com.alibaba.druid.util.StringUtils;
import com.example.config.valid.Group;
import com.example.domain.SysUser;
import org.springframework.stereotype.Service;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * UserService
 *
 * @Author: HanYong
 * @CreateTime: 2021-06-05
 */
@Service
public class UserService {

    public Map  insert(SysUser sysUser){
        String errorMsg = Validation.buildDefaultValidatorFactory()
                .getValidator()
                .validate(sysUser, Group.Create.class)
                .stream()
                .findFirst()
                .map(ConstraintViolation::getMessage).orElse(null);

        Map resultMap = new HashMap<>(16);
        if(StringUtils.isEmpty(errorMsg)){
            resultMap.put("code", 200);
            resultMap.put("msg", "新增成功");
        } else {
            resultMap.put("code", 401);
            resultMap.put("msg", errorMsg);
        }
        resultMap.put("sysUser", sysUser.toString());
        return resultMap;
    }
}

 

在Controller中添加下面代码

    @Autowired
    private UserService userService;

    /**
     * 新增用户
     *
     * @param sysUser 用户数据
     * @return 结果
     */
    @PostMapping("/insert")
    @ResponseBody
    public Map insert(SysUser sysUser) {
        return userService.insert(sysUser);
    }

 

进行测试

Spring Boot整合Validation进行参数校验_第6张图片

你可能感兴趣的:(Spring,Boot2.X,Validation,Spring参数校验)