SpringBoot 对DTO进行参数校验

本文介绍了如何在SpringBoot2.3及以上版本中集成参数校验,包括引入相关依赖、定义带有约束注解的实体类、使用RequestBody和表单方式验证,以及自定义校验规则和分组校验的详细步骤。
摘要由CSDN通过智能技术生成

SpringBoot 集成参数校验

第一步:引入依赖

注:从 springboot-2.3开始,校验包被独立成了一个 starter组件,所以需要引入validation和web,而 springboot-2.3之前的版本只需要引入 web 依赖就可以了。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--参数校验依赖2.3开始需单独引入-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

第二步:定义要参数校验的实体类

@Data
public class ValidVO {

    private String id;

    @NotNull(message = "appId不能为空")
    @Length(min = 6, max = 12, message = "appId长度必须位于6到12之间")
    private String appId;

    @NotBlank(message = "名字为必填项")
    private String name;

    @Email(message = "请填写正确的邮箱地址")
    private String email;

    @NotEmpty(message = "级别不能为空")
    private String level;
}

在实际开发中对于需要校验的字段都需要设置对应的业务提示,即message属性。
常见的约束注解如下:

注解功能
@AssertFalse可以为null,如果不为null的话必须为false
@AssertTrue可以为null,如果不为null的话必须为true
@DecimalMax设置不能超过最大值
@DecimalMin设置不能超过最小值
@Digits设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future日期必须在当前日期的未来
@Past日期必须在当前日期的过去
@Max最大不得超过此最大值
@Min最大不得小于此最小值
@NotNull不能为null,可以是空
@Null必须为null
@Pattern必须满足指定的正则表达式
@Size集合、数组、map等的size()值必须在指定范围内
@Email必须是email格式
@Length长度必须在指定范围内
@NotBlank字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range值必须在指定范围内
@URL必须是一个URL

第三步:定义校验类进行测试

1.RequestBody方式

示例:

对于json形式参数,需要在其前面加上@Validated 或 @Valid 注解

@PostMapping("test1")
public String test1(@Validated @RequestBody ValidVO validVO) {
    return "success";
}
测试结果:

image.png

2.表单方式

示例:
@PostMapping("test2")
public String test2(@Validated ValidVO validVO) {
    return "success";
}
测试结果:

TODO

3.单字段方式

示例:

单字段需要在Controller上加上 @Validated 注解,否则不生效

    @PostMapping("test3")
    public String test3(@Email String email) {
        return "test3";
    }
测试结果:

image.png

参数异常–全局异常处理器

首先继承 ResponseEntityExceptionHandler 并重写 handleMethodArgumentNotValid 方法

  • fieldName:获取异常的字段
  • defaultMessage:错误信息

此处可以自定义返回的逻辑,一般返回 defaultMessage 就够了

@Slf4j
@RestControllerAdvice
public class ValidateHandler extends ResponseEntityExceptionHandler {


    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        StringJoiner errorMessage = new StringJoiner("; ");
        for (var fieldError : ex.getBindingResult().getFieldErrors()) {
            // 获取字段名和错误信息,并转换为小写
            String fieldName = fieldError.getField().toLowerCase();
            String defaultMessage = fieldError.getDefaultMessage().toLowerCase();

            // 如果错误消息已经包含了字段名,则直接添加错误消息;否则拼接字段名和错误消息
            errorMessage.add(defaultMessage.startsWith(fieldName) ? fieldError.getDefaultMessage() : fieldName + " " + defaultMessage);
            break;
        }
        // 移除末尾多余的分号和空格
        String finalErrorMessage = errorMessage.toString().trim();

        // 返回统一的错误响应
        Result<Void> result = new Result<>(HttpStatus.BAD_REQUEST.value(), finalErrorMessage, null);
        return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
    }
}

自定义参数校验

性别校验

1.创建自定义注解

注解部分

  • @Retention(RUNTIME) 指定了该注解在运行时可见。
  • @Repeatable(EnumString.List.class) 指定了该注解可重复使用。
  • @Constraint(validatedBy = EnumStringValidator.class) 用于标明由哪个类执行校验逻辑。

参数部分

  • String message():默认的消息数据,校验失败默认提示信息。
  • Class<?>[] groups() 和 Class<? extends Payload>[] payload():用于框架分组和负载的设置。
  • String[] value():设置允许的枚举值。
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(SexValid.List.class)
@Constraint(validatedBy = SexValidator.class)   // 标明由哪个类执行校验逻辑
@Documented
public @interface SexValid {

    String message() default "value not in enum values";

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

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

    String[] value();

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        SexValid[] value();
    }
}
2.校验验证器

一般需要实现 ConstraintValidator 接口,并重写 initialize 和 isValid 方法。

  • initialize:用于验证器执行初始化时的操作,通过 constraintAnnotation 参数获取了注解中的枚举值,将其存储到 enumStrings 用于下一步的判断。
  • isValid:
    • 如果参数为空,则验证通过
    • 判断器是否在列表中,存在则通过,否则验证失败!
@Component
@Slf4j
public class SexValidator implements ConstraintValidator<SexValid, String> {

    private List<String> enumStrings;

    @Override
    public void initialize(SexValid constraintAnnotation) {
        enumStrings = Arrays.asList(constraintAnnotation.value());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        return enumStrings.contains(value);
    }
}
3.测试

将其加入到实体对象中并测试

@SexValid(value = {"男", "女"}, message = "性别必须为男/女")
private String sex;
测试结果:

image.png

ip地址校验

1.创建自定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IPAddressValidator.class)
@Documented
public @interface IPAddress {
    String message() default "{ipaddress.invalid}";

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

    Class<? extends Payload>[] payload() default {};
}
2.校验验证器
public class IPAddressValidator implements ConstraintValidator<IPAddress, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        try {
            InetAddress inetAddress = InetAddress.getByName(value);
            return inetAddress instanceof Inet4Address;
        } catch (UnknownHostException e) {
            return false;
        }
    }
}
3.测试
@IPAddress(message = "ip地址错误")
private String ipAddress;
测试结果

image.png

分组校验

1.定义分组接口

public interface ValidGroup extends Default {

    interface Crud extends ValidGroup {
        interface Create extends Crud {
        }

        interface Update extends Crud {
        }

        interface Query extends Crud {
        }

        interface Delete extends Crud {
        }
    }
}

2.给参数分配分组

  • 创建
    • id必须为空
  • 修改
    • id不能为空
    • sex不能为空且进行校验
@Data
public class ValidVO {

    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "应用Id不能为空")
    private String id;

    @SexValid(groups = ValidGroup.Crud.Update.class, value = {"男", "女"}, message = "性别必须为男/女")
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "性别不能为空")
    private String sex;

    @IPAddress(message = "ip地址错误")
    private String ipAddress;
}

3.验证

@PostMapping("add")
public String add(@Validated(value = ValidGroup.Crud.Create.class)
                  @RequestBody ValidVO validVO) {
    return validVO.toString();
}

@PostMapping("update")
public String update(@Validated(value = ValidGroup.Crud.Update.class)
                     @RequestBody ValidVO validVO) {
    return validVO.toString();
}

4.测试结果:

添加方法:

image.png

修改方法:

image.png

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零晚.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值