入参校验1

简介: 入参校验1

一、简介

1、快速失败(Fail Fast)

Spring Validation 默认会校验完所有字段,然后才抛出异常。但通常情况下我们希望遇到校验异常就立即返回,此时可以通过一些简单的配置,开启 Fali Fast 模式,一旦校验失败就立即返回。

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败模式
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

二、单字段类入参校验

这类主要依赖于@RequestParam进行入参的校验

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    @AliasFor("name")
    String value() default "";
    @AliasFor("value")
    String name() default "";
    boolean required() default true;
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

案例:

@GetMapping("/test")
public void test(@RequestParam(value = "id", required = true) String id,
                 @RequestParam(value = "name", required = false) String name) {
    log.info("id,:{}", id);
}
  • value:请求中传入参数的名称,如果不设置后台接口的value值,则会默认为该变量名。比如上图中第一个参数如果不设置value=“page”,则前端传入的参数名必须为pageNum,否则在后台接口中pageNum将接收不到对应的数据
  • required:该参数是否为必传项。默认是true,表示请求中一定要传入对应的参数,否则会报404错误,如果设置为false时,当请求中没有此参数,将会默认为null,而对于基本数据类型的变量,则必须有值,这时会抛出空指针异常。如果允许空值,则接口中变量需要使用包装类来声明。
  • defaultValue:参数的默认值,如果请求中没有同名的参数时,该变量默认为此值。注意默认值可以使用SpEL表达式,如"#{systemProperties[‘java.vm.version’]}"

三、JSON实体类校验

1、注解解析

validation-api中的注解

注解 说明 适用类型
@AssertFalse 限制必须是false boolean
Boolean:not null时才校验
@AssertTrue 限制必须是true boolean
Boolean:not null时才校验
@Max(value) 限制必须为一个小于等于value指定值的整数,value是long型 byte/short/int/long/float/double及其对应的包装类;
包装类对象not null时才校验
@Min(value) 限制必须为一个大于等于value指定值的整数,value是long型 byte/short/int/long/float/double及其对应的包装类;
包装类对象not null时才校验
@DecimalMax(value) 限制必须小于等于value指定的值,value是字符串类型 byte/short/int/long/float/double及其对应的包装类;
包装类对象not null时才校验
@DecimalMin(value) 限制必须大于等于value指定的值,value是字符串类型 byte/short/int/long/float/double及其对应的包装类;
包装类对象not null时才校验
@Digits(integer, fraction) 限制必须为一个小数(其实整数也可以),且整数部分的位数不能超过integer,小数部分的位数不能超过fraction。integer和fraction可以是0。 byte/short/int/long/float/double及其对应的包装类;
包装类对象必须not null时才校验
@Null 限制只能为null 任意对象类型(比如基本数据类型对应的包装类、String、枚举类、自定义类等);
不能是8种基本数据类型
@NotNull 限制必须不为null 任意类型(包括8种基本数据类型及其包装类、String、枚举类、自定义类等);
但是对于基本数据类型,没有意义
@Size(min, max) 限制Collection类型或String的长度必须在min到max之间,包含min和max Collection类型(List/Set)
String
@Pattern(regexp) 限制必须符合regexp指定的正则表达式 String
@Future 限制必须是一个将来的日期 Date/Calendar
@Past 限制必须是一个过去的日期 Date/Calendar
@Valid 校验任何非原子类型,标记一个对象,表示校验对象中被注解标记的对象(不支持分组功能) 需要校验成员变量的对象,比如@ModelAttribute标记的接口入参

2、案例

1、简单校验
@PostMapping("/test")
public void test(@Validated @RequestBody TestTableDTO dto) {
    log.info(JSON.toJSONString(dto));
}
import lombok.Data;
import javax.validation.constraints.*;
@Data
public class TestTableDTO {
    @NotNull(message = "id:为空")
    private Integer id;
    //  能通过("name":"",)
    @NotNull(message = "name:为空")
    private String name;
    @NotBlank(message = "address:为空")
    private String address;
    @Max(value = 10, message = "sex:大于10")
    private Integer sex;
    @Min(value = 90, message = "weight:小于90")
    private Integer weight;
    @Size(min = 120, max = 150, message = "height:范围120~150")
    private Integer height;
}
2、分组校验

分局不同的校验组,进行不同规则校验

@PostMapping("/test")
public void test(@Validated(value = InfoGroup.class) @RequestBody TestTableDTO dto) {
    log.info(JSON.toJSONString(dto));
}
@PostMapping("/get")
public void get(@Validated(value = StringGroup.class) @RequestBody TestTableDTO dto) 
    log.info(JSON.toJSONString(dto));
}

校验实体类

import lombok.Data;
import javax.validation.constraints.*;
@Data
public class TestTableDTO {
    @NotNull(message = "id:为空")
    private Integer id;
    @NotBlank(message = "address:为空",groups = InfoGroup.class)
    private String address;
    @Max(value = 10, message = "sex:大于10",groups = StringGroup.class)
    private Integer sex;
}

规则类InfoGroup

public interface InfoGroup {
}

规则类StringGroup

public interface StringGroup {
}
3、嵌套校验

Controller

@PostMapping("/test")
public void test(@Validated @RequestBody TestTableDTO dto) {
    log.info(JSON.toJSONString(dto));
}

实体类:

import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class TestTableDTO {
    @NotNull(message = "id:为空")
    private Integer id;
    @NotBlank(message = "address:为空", groups = InfoGroup.class)
    private String address;
    @Max(value = 10, message = "sex:大于10", groups = StringGroup.class)
    private Integer sex;
    @Valid
    private List<Course> course;
    @Data
    public static class Course {
        @NotBlank(message = "code:为空")
        private String code;
        @NotBlank(message = "name:为空")
        private String name;
    }
}

入参

{
    "id": 1,
    "name": "ddd",
    "address": "1",
    "sex": 9,
    "course": [
        {
            "code": "001",
            "name": "张三"
        },
        {
            "code": "002",
            "name": "李四"
        }
    ]
}
4、集合校验

如果接口请求体直接传递 JSON 数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果我们直接使用 java.util.Collection 下的 List 或者 Set 来接收数据,参数校验并不会生效。在这种情况下,我们需要使用自定义的 List 集合来接收参数,即包装 List 类型,并声明 @Valid 注解。

不校验类型(错误)

@PostMapping("/test")
public void test(@Validated @RequestBody List<TestTableDTO> dto) {
    log.info(JSON.toJSONString(dto));
}

正解

Controller

@PostMapping("/test")
public void test(@Validated @RequestBody StudentList<TestTableDTO> dto) {
    log.info(JSON.toJSONString(dto));
}

实体类:

import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class TestTableDTO {
    @NotNull(message = "id:为空")
    private Integer id;
    @NotBlank(message = "address:为空")
    private String address;
    @Max(value = 10, message = "sex:大于10")
    private Integer sex;
}

入参

[
    {
        "id": 1,
        "address": "1",
        "sex": 9
    },
    {
        "id": 1,
        "address": "",
        "sex": 9
    }
]

报错信息

系统异常:JSR-303 validated property 'list[1].address' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
5、自定义校验

自定义注解@ListValue

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
 * 自定义校验注解
 */
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class}) // 指定校验器,这里不指定时,就需要在初始化时指定
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
    /**
     * 默认的提示内容
     */
    String message() default "必须提交指定的值哦";
    /**
     * 校验分组
     */
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    /**
     * 默认值(用于校验器中,数值校验)
     */
    int[] values() default {};
}

自定义校验器ListValueConstraintValidator

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
/**
 * 指定校验器
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
    private final Set<Integer> initSet = new HashSet<>();
    /**
     * 初始化方法(将取注解默认值,存到指定位置)
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] values = constraintAnnotation.values();
        for (int value : values) {
            initSet.add(value);
        }
    }
    /**
     * 判断是否校验成功
     *
     * @param value   被校验参数
     * @param context 上下文
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        System.out.println(value);
        return initSet.contains(value);
    }
}

入参实体类

import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class TestTableDTO {
    @ListValue(message = "id:为空", values = 1)
    private Integer id;
    @NotBlank(message = "name:为空")
    private String name;
}

Controller层

@PostMapping("/test")
public void test(@Validated @RequestBody TestTableDTO dto) {
    log.info(JSON.toJSONString(dto));
}

测试

PostMan请求

{
    "id":null,
    "name":"张三"
}

测试结果:

四、相关

1、源码文件

CSDN

https://download.csdn.net/download/weixin_44624117/87796980

2、参考地址

目录
相关文章
|
4月前
|
文字识别
文本,文字识别13,参数校验,实现参数校验,使用@Valid注解
文本,文字识别13,参数校验,实现参数校验,使用@Valid注解
|
5月前
|
Java Spring 容器
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
|
6月前
|
前端开发 API 数据安全/隐私保护
如何优雅的进行入参数据校验?
【4月更文挑战第11天】如何优雅的进行入参数据校验?
|
6月前
|
SQL API 数据库
优雅地进行入参数据校验:场景和处理方式
在日常的开发工作中,入参数据校验是确保程序健壮性的关键步骤之一,我们需要确保请求中的数据类型、格式和取值范围符合要求,以保证接口的安全性和稳定性,还有就是传递给方法或函数的数据需要满足一定的规则和要求,以保证程序的正常运行和数据的有效处理。那么本文就来分享一下在哪些场景下进行入参数据校验,并分享一些优雅的处理方式,以提高代码的可读性、扩展性和复用性。
100 3
优雅地进行入参数据校验:场景和处理方式
|
6月前
|
数据格式 Python
添加 常用校验方法,校验常见数据格式
添加 常用校验方法,校验常见数据格式
63 0
|
6月前
|
数据格式 Python
添加 自定义校验方法,让用户自定义校验规则
添加 自定义校验方法,让用户自定义校验规则
76 0
|
前端开发
IE校验问题
IE校验问题
48 0
|
搜索推荐 小程序 NoSQL
如何设计API返回码(错误码)?
客户端请求 API,通常需要通过返回码来判断 API 返回的结果是否符合预期,以及该如何处理返回的内容等 相信很多同学都吃过返回码定义混乱的亏,有的 API 用返回码是 int 类型,有的是 string 类型,有的用 0 表示成功,又有的用 1 表示成功,还有用”true” 表示成功,碰上这种事情,只能说:头疼 API 返回码的设计还是要认真对待,毕竟好的返回码设计可以降低沟通成本以及程序的维护成本
261 0