Spring Validation 接口入参校验

这篇具有很好参考价值的文章主要介绍了Spring Validation 接口入参校验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

  • JSR 是 Java Specification Requests 的缩写,含义为 JAVA 规范提案。

  • JSR 303 - Bean Validation 规范, 正是一套基于 JavaBean 参数校验的标准。

  • Hibernate Validator 是 JSR 303 的实现,它提供了 JSR 303 规范中所有约束(constraint)的实现,同时也对其作出一些拓展。

  • Spring Validation 是对 Hibernate validation 的二次封装,用于支持 Spring MVC 参数校验。


😊 JSR 303 包含的注解:

验证注解

验证数据类型

说明

@Null

任意类型

元素值为 Null

@NotNull

任意类型

元素值不为 Null

@AssertTrue

Bool

元素为 true

@AssertFalse

Bool

元素为 false

@Min(value = 最小值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需大于等于指定值

@Max(value = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需小于等于指定值

@DecimalMin(value = 最小值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需大于等于指定值

@DecimalMax(value = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值需小于等于指定值

@Size(min = 最小值, max = 最大值)

String、Collection、Array 等

元素值的字符长度/集合大小需在指定的区间范围内

@Digits(integer = 整数位数, fraction = 小数位数)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值整数位、小数位需小于对应指定值

@Past

DATE、Calendar、Time 等日期

元素值需在指定时间之前

@Future

DATE、Calendar、Time 等日期

元素值需在指定时间之后

@Pattern(regexp = 正则式, flag = 标志的模式)

String 等

元素值与指定的正则式匹配

😊hibernate.validator 扩展的注解:

验证注解

验证数据类型

说明

@NotBlank

String 等 CharSequence 子类型

元素值不为空串(字符串不为 Null 且去除首尾空格后长度不为 0)

@Email(regexp = 正则式, flag = 标志的模式)

String 等

元素值为电子邮箱格式,可通过 regexp、flag 属性指定格式

@Length(min = 最小值, max = 最大值)

String 等

元素值长度在指定区间范围内

@NotEmpty

String、Collection、Array 等

元素值不为 Null 且长度不为 0

@Range(min = 最小值, max = 最大值)

BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (数字)子类型

元素值在指定区间范围内

@URL

String 等

元素值必须时合法的URL

二、Spring Validation的使用

1、在项目pom.xml中引入依赖

<!-- JSR 303 -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>

<!-- 在 SpringBoot 项目中,若 SpringBoot 版本小于 2.3.x ,则此依赖已包含在 spring-boot-starter-web 中,无需添加额外依赖;
若 SpringBoot 版本大于 2.3.x ,则需手动引入依赖。-->
<!-- Hibernate Validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

2、 参数注解校验的使用场景

场景一:直接入参的校验

①在类上添加 @Validated 注解,否则参数校验无法生效;

②入参中使用注解 @NotEmpty 等;

@Validated
@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/saveUserName")
    public String saveUserInfo(@NotEmpty String userName) {
        System.out.println("userName:"+ userName +",保存成功");
        return "success";
    }
}

测试:

【请求URL】:
http://192.168.1.7:27100/user/saveUserName?userName=

【执行结果】:
严重: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能为空] with root cause
javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能为空
    at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
……
场景二:对象模型属性的校验

1、创建 User对象

@Data
public class User {

    private Integer id;

    @NotEmpty(message = "用户名不能为空")
    private String userName;
    
    private Integer age;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")
    private String password;
}

2、全局异常捕获

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 方法直接入参校验
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BaseResult resolveConstraintViolationException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if (!CollectionUtils.isEmpty(constraintViolations)) {
            String errorMessage = constraintViolations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(", "));
            return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);
        }
        return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());
    }

    /**
     * 对象模型属性校验
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
        if (!CollectionUtils.isEmpty(allErrors)) {
            String errorMessage = allErrors.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(", "));
            return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);
        }
        return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());
    }
}

3、通用返回结构体

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BaseResult<T> {

    private Integer code;

    private String message;

    private T data;

    public static <T> BaseResult<T> success() {
        return new BaseResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), new JSONObject());
    }

    public static <T> BaseResult<T> fail(ResultEnum resultEnum, T data) {
        return fail(resultEnum.getCode(), resultEnum.getMsg(), data);
    }
    @Override
    public String toString() {
        return "BaseResult{" +
                "code=" + code +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

4、错误提示码枚举类

/**
 * 错误提示码
 */
public enum ResultEnum {
    /** 状态码:未知错误 **/
    UNKNOWN_ERROR(-1, "未知错误"),
    /** 状态码:成功 **/
    SUCCESS(0, "成功"),
    /** 状态码:失败 **/
    FAIL(1,"失败"),
    /** 状态码:非法参数 **/
    ILLEGAL_PARAMETER(2,"非法参数");

    private Integer code;

    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

}

5、暴露的接口

① 在类上添加 @Validated 注解,否则参数校验无法生效;

② 在入参实体前添加 @Validated 注解;

@Validated
@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/saveUserInfo")
    public String saveUserInfo(@RequestBody @Validated User user) {
        System.out.println("userName:"+ user.getUserName() +",保存成功");
        return "success";
    }
}

6、测试:

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{
   "userName":"张三"
}

【执行结果】:
{
     "code": 2,
     "message": "非法参数",
     "data": "用户密码不能为空"
}
场景三:参数注解校验的分组验证

1、新建分组接口

/**
 * 分组接口:新增
 */
public interface Add {

}

/**
 * 分组接口:更新
 */
public interface Update {

}

2、在校验注解中使用 groups 属性标明分组

@Data
public class User {

    @NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)
    private Integer id;

    @NotEmpty(message = "用户名不能为空")
    private String userName;
    
    private Integer age;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")
    private String password;
}

【注】需要注意的是实体类中所有字段注解校验规则默认属于 Default 分组,接口入参 @Validated 注解默认检验 Default 分组的校验规则。当接口入参使用 @Validated 显式声明非默认分组时,实体类中所有未显式声明分组的注解校验将不会生效。

3、处理器方法

@Validated
@RestController
@RequestMapping("/user")
public class UserController {
    @PostMapping("/updateUserInfo")
    public String updateUserInfo(@RequestBody @Validated({Update.class}) User user) {
        System.out.println("id:" + user.getId() + ",userName:"+ user.getUserName() +",更新成功");
        return "success";
    }
}

4、测试

【请求URL】:http://192.168.1.7:27100/user/updateUserInfo
【请求方式】:POST
【请求参数】:
{
  "id":1,
  "userName":"李四"
}

【执行结果】:
id:1,userName:李四,更新成功

【注】入参中没有传“密码”,也没有校验,若默认属于 Default 分组的字段也需要校验,可以写成:

public String updateUserInfo(@RequestBody @Validated({Update.class, Default.class}) User user) {
场景四:级联校验

1、在实体类模型中,可能会存在集合类型或实体类型的成员变量,该成员中的字段仍然需要校验。

在要校验的对象类型的属性/ list上使用 @Valid 注解:

@Data
public class User {

    @NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)
    private Integer id;

    @NotEmpty(message = "用户名不能为空")
    private String userName;
    
    private Integer age;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")
    private String password;

    @Valid
    private List<Car> cars;
}


@Data
public class Car {

    @NotNull(message = "车牌号码不能为空")
    private String plateCode; 

    @NotNull(message = "车牌颜色不能为空")
    private String plateColor;
}

2、处理器方法

@Validated
@RestController
@RequestMapping("/user")
public class UserController {
    @PostMapping("/saveUserInfo")
    public String saveUserInfo(@RequestBody @Validated User user) {
        System.out.println("userName:"+ user.getUserName() +",保存成功");
        return "success";
    }
}

3、测试

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{
        
    "userName": "李四",
    "password": "123456",
    "cars": [
        {
            "plateCode": "京A0001",
            "plateColor": "1"
        },
                {
            "plateCode": "京A0002"
        }
    ]
}

【执行结果】:
{
     "code": 2,
     "message": "非法参数",
     "data": "车牌颜色不能为空"
}
场景五:自定义注释校验

如下:校验用户下车牌号码必须包含“京A”,否则返回提示;

1、自定义注释

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {MustContainKeyValidator.class})
public @interface MustContainKey {

    //默认错误信息
    String message() default "必须含有指定关键字";
 
   //分组
    Class<?>[] groups() default {};

    // 负载
    Class<? extends Payload>[] payload() default {};
}

2、真正的校验者类,实现 ConstraintValidator 接口

public class MustContainKeyValidator implements ConstraintValidator<MustContainKey,String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (!StringUtils.isEmpty(value) && !value.contains("京A")) {
            // 获取默认提示消息
            String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
            System.out.println(defaultConstraintMessageTemplate);

            // 禁用默认提示信息
            // context.disableDefaultConstraintViolation();

            //设置提示语
            // context.buildConstraintViolationWithTemplate("must contain key").addConstraintViolation();

            return false;
        }
        return true;
    }
}

3、在实体类的属性上添加该注释

@Data
public class User {

    @NotNull(message = "更新数据时,主键id不能为空", groups = Update.class)
    private Integer id;

    @NotEmpty(message = "用户名不能为空")
    private String userName;
    
    private Integer age;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 5, max = 10,message = "密码长度必须是5-10个字符")
    private String password;

    @Valid
    private List<Car> cars;
}


@Data
public class Car {
    
    @MustContainKey
    @NotNull(message = "车牌号码不能为空")
    private String plateCode; 

    @NotNull(message = "车牌颜色不能为空")
    private String plateColor;
}

4、测试

【请求URL】:http://192.168.1.7:27100/user/saveUserInfo
【请求方式】:POST
【请求参数】:
{
        
    "userName": "李四",
    "password": "123456",
    "cars": [
        {
            "plateCode": "京A0001",
            "plateColor": "1"
        },
        {
            "plateCode": "浙C0002",
            "plateColor": "2"
        }
    ]
}
【执行结果】:
{
     "code": 2,
     "message": "非法参数",
     "data": "必须含有指定关键字"
}

以上就是所有关于 Spring Validation 的介绍,感兴趣的同学欢迎点赞+收藏!文章来源地址https://www.toymoban.com/news/detail-472158.html

Spring Validation 接口入参校验

到了这里,关于Spring Validation 接口入参校验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 完美实现校验:利用Spring Validation实现强大的输入验证

    校验例子 大家平时编码中经常涉及参数的校验,对于一个用户注册的方法来说会校验用户名密码信息: 上述例子中需要手动编写参数校验逻辑的过程。虽然对于这个简单的示例而言,手动编写校验逻辑可能是可行的,但是对于复杂的验证规则和多个参数的情况,手动编写校

    2024年01月20日
    浏览(37)
  • spring6-国际化:i18n | 数据校验:Validation

    1.1、i18n概述 国际化也称作i18n,其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数。由于软件发行可能面向多个国家,对于不同国家的用户,软件显示不同语言的过程就是国际化。通常来讲,软件中的国际化是通过配置文件来实现的,假设要支撑两种语言,

    2024年02月08日
    浏览(46)
  • Springboot实现优雅的参数校验(Spring Validation)和 if else说再见

    当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数,写了一大堆的 if else 进行校验,非常的不优雅,比起枯燥的CRUD来说,参数校验更是枯燥。这只是一个创建菜单的

    2023年04月15日
    浏览(39)
  • 一起学SF框架系列5.11-spring-beans-数据校验validation

        在日常的项目开发中,应用在执行业务逻辑之前,为了防止非法参数对业务造成的影响,必须通过校验保证传入数据是合法正确的,但很多时候同样的校验出现了多次,在不同的层,不同的方法上,导致代码冗余,违反DRY原则。     Java提供了数据校验规范来解

    2024年02月15日
    浏览(37)
  • 【Spring框架】--04.单元测试JUnit、事务、资源操作Resources、国际化、数据校验Validation、提前编译AOT

    学习视频: 尚硅谷Spring教程 在之前的测试方法中,几乎都能看到以下的两行代码: 这两行代码的作用是创建Spring容器,最终获取到对象,但是每次测试都需要重复编写。针对上述问题,我们需要的是程序能自动帮我们创建容器。我们都知道JUnit无法知晓我们是否使用了 Spr

    2024年02月12日
    浏览(39)
  • Spring Boot中使用validator如何实现接口入参自动检验

    在项目开发过程中,经常会对一些字段进行校验,比如字段的非空校验、字段的长度校验等,如果在每个需要的地方写一堆if else 会让你的代码变的冗余笨重且相对不好维护,如何更加规范和优雅的校验呢? Spring Boot中可以使用Validation Api和Hibernate Validator实现接口入参自动检

    2024年02月12日
    浏览(41)
  • java的validation框架(参数校验)

    空值校验类:@Null,@NotNull,@NotEmpty,@NotBlank等 范围校验类:@Min,@Size,@Digits,@Future,@Negative等 其它校验类:@Email,@URL,@AssertTrue,@Pattern等 1. @NotNull(message = “用户id不能为空”) 没有设置值时会返回 设置为userInfo.setUserId(\\\"\\\");时不会返回 userInfo.setUserId(\\\" \\\");时不会返回 2.@Not

    2024年01月22日
    浏览(47)
  • java使用Validation进行数据校验

    在开发中,我们经常遇到参数校验的需求,比如用户注册的时候,要校验用户名不能为空、用户名长度不超过20个字符、手机号是合法的手机号格式等等。如果使用普通方式,我们会把校验的代码和真正的业务处理逻辑耦合在一起,而且如果未来要新增一种校验逻辑也需要在

    2024年02月06日
    浏览(35)
  • 身份证号码,格式校验:@IdCard(Validation + 原生实现校验逻辑)

    自定义一个用于校验 身份证号码 格式的注解 @IdCard ,能够和现有的 Validation 参数校验机制兼容,使用方式和其他校验注解保持一致(使用 @Valid 注解接口参数)。 本文使用原生方式实现校验逻辑,校验规则的实现较为基础;Hutool工具提供了更加完善的校验工具,可以考虑使

    2024年02月07日
    浏览(43)
  • 文件导入之Validation校验List对象数组

    背景: 我们的接口是一个List对象,对象里面的数据基本都有一些基础数据校验的注解,我们怎么样才能校验这些基础规则呢? 我们在导入excel文件进行数据录入的时候,数据录入也有基础的校验规则,这个时候我们又该如何少写代码让Validation框架来帮我们完成这些基础校验

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包