首页 > 其他分享 >springboot2 - validation

springboot2 - validation

时间:2024-05-21 09:53:29浏览次数:25  
标签:validation javax 校验 class springboot2 Validated public

业务需求:客户端提交的表单,后台需要有统一的校验拦截机制。

Maven 依赖

除了 hibernate-validator,springboot 本身自带这些依赖。


<dependencys>
    <dependency>
        <groupId>jakarta.validation</groupId>
        <artifactId>jakarta.validation-api</artifactId>
        <version>2.0.2</version>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.1.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.5.Final</version>
        <scope>compile</scope>
    </dependency>
</dependencys>

springboot-2.3 以上,需要手动引入依赖。

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

@Validated 和 @Valid 区别

  • javax 提供了@Valid(标准JSR-303规范),而 @Validated 是由 spring 提供的变种,多了个分组的功能。

  • @Validated 的可用位置:类、方法、参数(ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER)

  • @Valid 可用位置更多,多了这些:构造函数、java8新特性支持(ElementType.CONSTRUCTOR, ElementType.TYPE_USE)。

常用方式

我们一般使用下面这种方式进行数据校验。


@Controller
@RequestMapping("debug")
public class DebugCtrl {

    // 参数是一个对象,需要在参数上加 @Validated 或者 @Valid
    @ResponseBody
    @RequestMapping("valid")
    public Result valid(@Validated AppInfo info) {
        System.out.println(info);
        return Result.failed();
    }
}

验证单个字段,需要在类上增加 @Validated,这种情况使用 @Valid 是无效的。

用起来挺别扭的,只是验证是否为空,不如 @RequestParam,我一般选择自己编码增强这一逻辑。


// 需要在整个类上增加 @Validated
@Validated
@Controller
@RequestMapping("debug")
public class DebugCtrl {

    // 验证一个字段
    @ResponseBody
    @RequestMapping("valid")
    public Result valid(@NotEmpty String a) {
        return Result.failed();
    }
}

分组的使用

在业务的不同阶段,对象验证规则不一样,这时候,就需要用到分组功能。

场景:对于自增 ID,新增的时候,ID 不能有值,修改的时候,ID 不允许为空。


public class AppInfo implements Serializable {

    // version 字段最大长度是 10,指定新增、修改时有效
    @Size(groups = {Insert.class, Update.class}, max = 10)
    private String version;

    // 省略其它代码 …………
}

class Test {

    // 使用工具包校验,验证 groups 中带有 Insert.class 的字段
    public static void main(String[] args) throws BindException {
        AppInfo appInfo = new AppInfo();
        appInfo.setVersion("1adasdadadadasd32123");
        ValidationUtils.validate(appInfo, Insert.class);
    }

    // 使用 @Validated 校验,验证 groups 中带有 Insert.class 的字段
    @ResponseBody
    @RequestMapping("valid")
    public Result valid(@Validated(Insert.class) AppInfo info) {
        System.out.println(info);
        return Result.failed();
    }
}

个人并不推荐使用 @Validated 的分组功能:

  1. 容易被滥用:如果一个对象有很多套校验规则,使用分组的方式实现,代码就会变得十分混乱;
  2. 容易产生歧义:有的字段,指定了分组,有的不指定,对于不熟悉的人,无法预知程序的结果。

个人比较推荐:通用部分,在对象中注明,特殊的字段,在 Controller 层进行单独强调。

public class AppInfo implements Serializable {

    // @NotEmpty 没有分组配置,本意可能是希望任何场景都生效,
    // 但实际与之相反,@Validated 指定了分组,则会跳过 @NotEmpty 的校验。 
    @NotEmpty
    @Size(groups = {Insert.class, Update.class}, max = 10)
    private String version;
}

工具包

有些情况下需要手动进行校验,比如:Excel 导入过程中,需要批量校验数据,这种情况,手动校验更有利于组织代码。

可以参考下面这段代码,校验失败会抛出 BindException 异常,这与 spring 默认行为是一致的,你只要写一个统一的异常处理切面即可。


import cn.seaboot.commons.core.CommonUtils;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * Java 对象校验工具
 *
 * @author Mr.css
 * @version 2023-09-21 11:12
 */
public class ValidationUtils {
    private static final Validator validator;

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

    /**
     * javax.validation 注解校验,分组校验
     * <p>
     * 如果校验失败,则抛出异常,spring 环境下可以写一个统一的异常捕获切面
     *
     * @param obj    校验实体
     * @param groups 校验分组
     * @throws ConstraintViolationException 抛出验证失败异常
     */
    public static void validate(Object obj, Class<?>... groups) {
        Set<ConstraintViolation<Object>> set = validator.validate(obj, groups);
        if (CommonUtils.isNotEmpty(set)) {
            throw new ConstraintViolationException(set);
        }
    }
}

需要处理的异常

出现校验不通过,一般会抛出下面两种异常:

  • org.springframework.validation.BindException:spring 环境下会抛出的异常
  • javax.validation.ConstraintViolationException:javax 原生验证会触发的异常

对于异常的处理,下面提供代码样例,根据自身项目需求进行调整。

class Test {
    /**
     * Handler BindException (from: hibernate-validator)
     */
    public ModelAndView handlerBindException(HttpServletRequest request, HttpServletResponse response, BindException ex) {
        BindingResult result = ex.getBindingResult();
        if (result.hasErrors()) {
            List<FieldError> errors = result.getFieldErrors();
            Map<String, Object> object = new HashMap<>(errors.size());
            for (FieldError error : errors) {
                object.put(error.getField(), error.getDefaultMessage());
            }
            return new Result(HttpStatus.INTERNAL_SERVER_ERROR.value(), object).toView();
        } else {
            return Result.INTERNAL_SERVER_ERROR.toView();
        }
    }

    /**
     * Handler ConstraintViolationException (from: javax.validation)
     */
    public ModelAndView handlerConstraintViolationException(HttpServletRequest request, HttpServletResponse response, ConstraintViolationException ex) throws IOException {
        Set<ConstraintViolation<?>> set = ex.getConstraintViolations();
        Map<String, Object> object = new HashMap<>(set.size());
        for (ConstraintViolation<?> violation : set) {
            object.put(violation.getPropertyPath().toString(), violation.getMessage());
        }
        return new Result(HttpStatus.INTERNAL_SERVER_ERROR.value(), object).toView();
    }
}

相关源码

  • org.springframework.validation.beanvalidation.MethodValidationPostProcessor
  • org.springframework.validation.beanvalidation.MethodValidationInterceptor

标签:validation,javax,校验,class,springboot2,Validated,public
From: https://www.cnblogs.com/chenss15060100790/p/18203331

相关文章

  • springboot2 - lettuce
    spring操作redis,默认使用的是lettuce,介绍一下相关代码。Maven依赖<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>......
  • springboot2 - ehcache
    介绍ehcache一下在spring环境下的应用。如果是单机系统,ehcache一般是首选方案,想通过切换redis提高性能,意义不大,反而会增加部署和维护负担。工具函数如果想在spring环境下,封装自己的工具函数,下面这些基础代码估计会用到。场景:像是Excel导入数据,需要大批量更新缓存时......
  • springboot2 - 请求相关的兼容配置
    StandardServletMultipartResolverStandardServletMultipartResolver在spring4和spring5代码是不一样的。在低版本spring环境下,文件只能通过POST请求提交。对程序的影响可能不大,因为现在的做法,基本形成统一的定式:文件表单和业务表单分离,先将文件上传,返回一段url,再将......
  • validation捕获异常
    好像需要在控制器启用校验才能捕获参数校验,即@RestController@Validated----------------------分隔符-----------------------------importjakarta.validation.ConstraintViolation;importjakarta.validation.ConstraintViolationException;importorg.springframework.......
  • 宝塔:续签SSL证书报错error_result: During secondary validation
    宝塔上的SSL证书有时候忘了到期前续签,导致续签时候报错:error_result:Duringsecondaryvalidation:或者 该帐户1小时内失败的订单次数超过5次,请等待1小时再重试! 点击SSL当前证书,续签证书处理,是无效的。因此要换种方式: 解决方法如下:1.点击SSL选项框内的“证书夹”选项,删......
  • WPF ValidatesOnDataErrors IDataErrorInfo ValidationRule
    //xaml<Windowx:Class="WpfApp91.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mic......
  • ES Validation Failed: 1: this action would add [1] shards, but this cluster c
    [2024-05-01T08:56:52,606][ERROR][o.e.x.i.IndexLifecycleRunner][tools]policy[ilm-history-ilm-policy]forindex[.ds-ilm-history-5-2024.03.28-000001]failedonstep[{"phase":"hot","action":"rollover","name&qu......
  • SpringBoot2.x整合Redis Sentinel
    redissentinel搭建之后,在spring-boot项目中集成。配置在pom.xml文件中添加如下依赖配置(这里spring-boot版本2.2.5),这个版本中,默认使用lettuce作为redis连接池。<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis<......
  • 9.prometheus监控--监控springboot2.x(Java)
    一、环境部署yumsearchjava|grepjdkyuminstall-yjava-11-openjdk-devel二、监控java应用(tomcat/jar)JMXexporter负责收集Java虚拟机信息---没举例,等以后再做测试进入到tomcat安装目录,vimPROMETHEUS_JMX_EXPORTER_OPTS="-javaagent:../prometheus-exporter......
  • FluentValidation
    FluentValidation—FluentValidationdocumentation publicclassCustomerValidator:AbstractValidator<Customer>{publicCustomerValidator(){RuleFor(x=>x.Surname).NotEmpty();RuleFor(x=>x.Forename).NotEmpty().WithMessage(&q......