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

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

概述

校验例子

大家平时编码中经常涉及参数的校验,对于一个用户注册的方法来说会校验用户名密码信息:

public class UserController {

    public ResponseEntity<String> registerUser(String username, String password) {
        if (username == null || username.isEmpty()) {
            return ResponseEntity.badRequest().body("用户名不能为空");
        }

        if (password == null || password.isEmpty()) {
            return ResponseEntity.badRequest().body("密码不能为空");
        }

        if (password.length() < 6) {
            return ResponseEntity.badRequest().body("密码长度至少为6位");
        }

        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
}

上述例子中需要手动编写参数校验逻辑的过程。虽然对于这个简单的示例而言,手动编写校验逻辑可能是可行的,但是对于复杂的验证规则和多个参数的情况,手动编写校验逻辑会变得冗长、难以维护和复用。

引入现代的校验框架如Spring Validation可以帮助解决这些问题,提供更高效、统一和可维护的参数校验方案。

Bean Validation规范

  • JSR303/JSR-349/JSR-380: JSR303(Bean Validation)是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349(Bean Validation 1.1)是其的升级版本,添加了一些新特性。JSR-380(Bean Validation 2.0,JSR380标准 )对其规范进一步扩展和增强。

  • hibernate validationhibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等

  • Spring validationspring validationhibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中

Bean Validation的主页:http://beanvalidation.org  
Bean Validation的参考实现:https://github.com/hibernate/hibernate-validator

相关版本兼容性

Bean Validation Hibernate Validation JDK Spring Boot
1.1 5.4 + 6+ 1.5.x
2.0 6.0 + 8+ 2.0.x
3.0 7.0 + 9+ 2.0.x

3.0后Bean Validation改名为Jakarta Bean Validation 3.0了。
如果你的项目版本是jdk1.8的,不要使用hibernate-validator 7.0的版本,它里面的依赖的jakarta.validation-api:3.0是需要jdk1.9的部分支持的。

Spring Validation注解

Spring Validation建立在Java Bean Validation(JSR 380)的基础上,为开发人员提供了一组注解和工具,用于定义和执行数据验证规则。它允许开发人员在应用程序中定义验证规则,并使用这些规则来验证输入数据、请求参数、领域对象等。

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

常用注解标签如下:

标签 说明
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

hibernate-validator 校验Java Bean

  • pom引入hibernate-validator

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>
  • 创建一个Java Bean,我们校验一下用户名跟年龄

public class User {

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

    @Min(value = 18, message = "年龄不能小于18岁")
    private int age;

    // 构造函数、Getter 和 Setter 方法
}
  • 执行校验

public class ValidatorTest {

    public static void main(String[] args) {
        // 创建校验器
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        // 创建用户对象
        User user = new User();
        user.setUsername("");
        user.setAge(16);

        // 执行校验
        Set<ConstraintViolation<User>> violations = validator.validate(user);

        // 处理校验结果
        if (!violations.isEmpty()) {
            for (ConstraintViolation<User> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("校验通过");
        }
    }
}

用Spring Validation提高生产力

Spring Validation引入

添加pom依赖

Spring Validation校验包被独立成了一个starter组件,引入如下依赖:

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

如果是spring-boot-starter-web不用引入了,spring-boot-starter-web 集成了spring-boot-starter-validation,默认可以不加spring-boot-starter-validation,它同时也集成了hibernate-validator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
hibernate Validator校验器

这里我们定义hibernate校验器用于校验参数

    @Configuration
    @EnableAutoConfiguration
    public class HibernateValidatorConfiguration {
        @Bean
        public MethodValidationPostProcessor methodValidationPostProcessor() {
            MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
            processor.setValidator(validator());
            processor.setProxyTargetClass(true);
            return processor;
        }

        @Bean
        public Validator validator() {
            return Validation
                    .byProvider(HibernateValidator.class)
                    .configure()
                    .addProperty("hibernate.validator.fail_fast", "true")
                    .buildValidatorFactory()
                    .getValidator();
        }
    }
全局异常处理

每个Controller方法中都如果都写一遍对校验结果信息的处理,使用起来还是很繁琐。可以通过全局异常处理的方式统一处理校验异常。

    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
        @ExceptionHandler(value = ConstraintViolationException.class)
        @ResponseBody
        public ApiResult defaultInsuranceExceptionHandler(ConstraintViolationException e) {
            log.error("校验错误:{}", e.getMessage());
            return ApiResult.error("error", e.getMessage());
        }


        @ExceptionHandler(value = MissingServletRequestParameterException.class)
        @ResponseBody
        public ApiResult handleMissingServletRequestParameter(MissingServletRequestParameterException ex) {
            String error = ex.getParameterName() + " 参数为空";
            return ApiResult.error("error", error);
        }
    }

Controller接口Bean校验

首先,假设我们有一个用户注册的请求对象 UserRegistrationRequest,其中包含用户名和密码字段。

@Data
public class UserRegistrationRequest {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少为6位")
    private String password;
}

我们使用了两个注解进行参数校验:

  • @NotBlank:该注解用于验证字段不能为空或空格,并可以通过 message 属性指定验证失败时的错误消息。

  • @Size:该注解用于验证字段的长度,我们指定了密码的最小长度为6,并通过 message 属性定义了验证失败时的错误消息。

接下来我们定义一个Controller接口

@RestController
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserRegistrationRequest request) {
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
}

@RequestParam 参数校验

首先需要将MethodValidationPostProcessor设置成cglib代理

processor.setProxyTargetClass(true);

定义一个接口,我们需要对ID进行校验,1<=id<=400

@Validated
public interface ValidClient {

    @GetMapping("/valid")
    String queryById(@RequestParam("id") @Min(1) @Max(400) Integer id);

}

controller实现

@RestController
public class ValidController implements ValidClient {

    @Override
    public String queryById(Integer id) {
        return "id:" + id;
    }
}

分组校验

还是看上面那个Controller接口校验的例子,如果我们注册的时候需要校验用户名和密码,重置密码的时候只校验密码该怎么校验呢?这个时候就用到了分组校验了。

  • 首先,我们需要定义一个新的分组,用于更新场景中的验证,例如UpdateGroup

public interface UpdateGroup {
}
  • 接下来,我们需要在UserRegistrationRequest类的username字段上使用@Validated注解,并通过groups属性指定要应用的验证分组。

public class UserRegistrationRequest {

    @NotBlank(message = "用户名不能为空", groups = {RegistrationGroup.class})
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少为6位")
    private String password;

    // 其他字段和方法
}

在上述示例中,我们将@Validated注解应用到username字段,并通过groups属性指定了RegistrationGroup.class,这意味着在注册场景中会进行校验。

  • 我们可以使用@Validated注解来指定不要应用任何验证分组。

@RestController
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Validated(RegistrationGroup.class) @RequestBody UserRegistrationRequest request) {
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }

    @PostMapping("/update")
    public ResponseEntity<String> updateUser(@Validated(UpdateGroup.class) @RequestBody UserRegistrationRequest request) {
        // 处理用户更新逻辑
        return ResponseEntity.ok("用户更新成功");
    }
}

在上述示例中,我们在updateUser方法中使用@Validated(UpdateGroup.class)来指定在更新场景中只执行UpdateGroup分组的验证规则。由于username字段上没有指定groups属性,所以在更新场景中将不会对username字段进行校验。

自定义注解

Spring 的 validation 为我们提供了许多特性,几乎可以满足日常开发中绝大多数参数校验场景了。但是,一个好的框架一定是方便扩展的。有了扩展能力,就能应对更多复杂的业务场景,下面我们自定义一个日期格式校验的注解

定义注解接口

    @Documented
    @Constraint(validatedBy = DateFormatValidator.class)
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DateFormat {
        //默认错误消息
        String message() default "时间格式错误";

        //分组
        Class<?>[] groups() default {};

        //默认日期格式
        String formatter() default "yyyy-MM-dd";

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

        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @interface List {
            DateFormat[] value();
        }
    }

定义校验器

public class DateFormatValidator implements ConstraintValidator<DateFormat, String> {
    protected String dateFormatter;

    @Override
    public void initialize(DateFormat constraintAnnotation) {
            this.dateFormatter = constraintAnnotation.formatter();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
            if (StringUtils.isNotBlank(value)) {
                    try {
                            DateUtils.parseDate(value, dateFormatter);
                    } catch (Exception e) {
                            return false;
                    }
                    return true;
            }
            return false;
    }
}

自定义校验注解使用起来和官方注解没有区别,在需要的字段上添加相应注解即可。文章来源地址https://www.toymoban.com/news/detail-808236.html

public class RequestParam {
    @DateFormat(message = "日期输入错误")
    private String beginDate;

    // 其他字段和方法
}

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

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

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

相关文章

  • Spring Validation 接口入参校验

    JSR 是 Java Specification Requests 的缩写,含义为 JAVA 规范提案。 JSR 303 - Bean Validation 规范, 正是一套基于 JavaBean 参数校验的标准。 Hibernate Validator 是 JSR 303 的实现,它提供了 JSR 303 规范中所有约束(constraint)的实现,同时也对其作出一些拓展。 Spring Validation 是对 Hibernate validati

    2024年02月07日
    浏览(73)
  • 参数校验: spring-boot-starter-validation

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

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

    2024年02月08日
    浏览(38)
  • 一起学SF框架系列5.11-spring-beans-数据校验validation

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

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

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

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

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

    2024年02月12日
    浏览(29)
  • 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日
    浏览(38)
  • java使用Validation进行数据校验

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

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

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

    2024年02月09日
    浏览(32)
  • @NotNull @NotEmpty @NotBlank java校验注解 validation

    1.@NotNull 不能为 null,但可以为 empty,一般用于判空 Integer 类型等基本数据类型,而且被其标注的字段可以使用 @size、@Max、@Min 对数值进行大小的控制 2.@NotEmpty 不能为 null,且长度必须大于 0,一般用于集合类或者数组上,也有人用于String(不推荐) 3.@NotBlank 只能作用在接收的

    2024年02月10日
    浏览(31)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包