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依赖

<!--参数校验-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

 

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

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<String, Object> result(String errorMsg) {
        Map<String, Object> 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<String, Object> submitLogin1(String username, @NotNull(message = "密码不能为空")String password, String captcha) {
        Map<String, Object> 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;
    }
}

 

 

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

@Data
public class LoginUser {

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

    private String password;

    private String captcha;
}

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

    /**
     * 提交登陆信息
     *
     * @return 结果
     */
    @PostMapping("/login3")
    @ResponseBody
    public Map<String, Object> submitLogin3(@Validated LoginUser loginUser) {
        Map<String, Object> 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;
    }

 

 

三、常用注解介绍

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<? extends Payload>[] 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<Equals, String> {

    private String value2;

    /**
     * Initializes the validator in preparation for
     * {@link #isValid(String, ConstraintValidatorContext)} calls.
     * The constraint annotation for a given constraint declaration
     * is passed.
     * <p>
     * This method is guaranteed to be called before any use of this instance for
     * validation.
     * <p>
     * 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.
     * <p>
     * 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<String, Object> submitLogin1(@Equals(val = "zhangsan", message = "账号错误")String username, String password, String captcha) {
        Map<String, Object> 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;
    }

 

 

五、分组校验

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

 

创建一个分组类

/**
 * 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<String, Object> insert(@Validated(Group.Create.class) SysUser sysUser) {
        Map<String, Object> 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<String, Object> modify(@Validated(Group.Modify.class) SysUser sysUser) {
        Map<String, Object> resultMap = new HashMap<>(16);
        resultMap.put("code", 200);
        resultMap.put("msg", "修改成功");
        resultMap.put("modifyRoleSet", sysUser.toString());
        return resultMap;
    }

 

六、手动获取校验结果

之前我们都是在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<String, Object>  insert(SysUser sysUser){
        String errorMsg = Validation.buildDefaultValidatorFactory()
                .getValidator()
                .validate(sysUser, Group.Create.class)
                .stream()
                .findFirst()
                .map(ConstraintViolation::getMessage).orElse(null);

        Map<String, Object> 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<String, Object> insert(SysUser sysUser) {
        return userService.insert(sysUser);
    }

 

进行测试

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot中,可以使用Spring Boot Validation来对参数名称进行校验。通过引入spring-boot-starter-validation依赖,可以使用Spring Validator对参数进行校验Spring Validator是对Hibernate Validator的进一步封装,同时支持Spring MVC的自动校验。你可以在Spring官方文档中找到更多关于Spring Boot Validation的详细信息\[2\]。 在使用Spring Boot Validation时,可以使用@Validated注解来标记需要校验的类型、方法和方法参数。@Validated注解属于org.springframework.validation.annotation包,是Spring校验机制之一。它具有分组校验的功能,可以用于类型、方法和方法参数上,但不能用于成员属性(field)\[3\]。 通过使用Spring Boot Validation,你可以简化参数校验的代码,提高代码的可读性和美观性。 #### 引用[.reference_title] - *1* *3* [Spring Boot参数校验](https://blog.csdn.net/qq1929892209/article/details/126133350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring Boot 参数校验校验工具类](https://blog.csdn.net/WEDUEST/article/details/121594610)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值