SpringBoot 参数校验

一、参数校验处理逻辑

在Spring Boot中,实现参数校验主要依赖于Java Bean Validation API(JSR 380),以及Spring框架对该API的集成支持。以下是具体步骤:

  1. 引入依赖:首先确保项目中引入了Spring Validation相关的依赖。如果使用的是Spring Boot 2.3.x之前的版本,spring-boot-starter-web会自动包含hibernate-validator。对于2.3.x及以后的版本,可能需要手动添加spring-boot-starter-validation依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  1. 定义校验约束:使用Hibernate Validator提供的注解来标记你想要校验的字段。例如,@NotNull表示该字段不能为null,@Size(min=3, max=50)表示字符串长度必须在3到50个字符之间,@Email用于校验邮箱格式等。
public class User {
    @NotNull(message = "用户名不能为空")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    // getter和setter方法
}
  1. 控制器层校验:在控制器方法的参数上使用@Valid注解,这样Spring MVC会自动触发参数校验。如果校验失败,会抛出MethodArgumentNotValidException异常。
@PostMapping("/user")
public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
    // 创建用户逻辑
    return ResponseEntity.ok().build();
}
  1. 全局异常处理:通过创建一个@ControllerAdvice类来统一处理校验异常,返回标准化的响应。
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, Object> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        Map<String, Object> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}
  1. 自定义校验注解:如果内置注解无法满足需求,可以创建自定义注解和相应的校验器。

二、常用校验注解

JSR 380 (Bean Validation API) 提供了一系列基本校验注解,用于在Java Bean属性上执行不同类型的校验。下面是一些常用的校验注解及其说明:

注解描述说明
@NotNull验证指定的元素不为null
@Null验证指定的元素必须为null
@NotBlank验证字符串字段非null,且长度大于0,不只包含空白字符
@NotEmpty验证集合、数组、Map或字符串不为null,且不为空
@Size(min=, max=)验证字符串、集合、数组或Map的大小在特定范围内
@Length(min=, max=)验证注解的元素值长度是否在min和max区间内
@Range(min=, max=)验证注解的元素值在min和max之间
@Min验证数字类型的字段值必须大于等于指定的最小值
@Max验证数字类型的字段值必须小于等于指定的最大值
@DecimalMin验证数字类型的字段值必须大于等于指定的最小值,支持小数点
@DecimalMax验证数字类型的字段值必须小于等于指定的最大值,支持小数点
@Pattern验证字符串是否符合正则表达式的模式
@Future检查日期类型的字段值是否在当前时间之后
@Past检查日期类型的字段值是否在当前时间之前
@Email检查字符串字段是否是有效的电子邮件地址
@AssertFalse验证注解的元素值是false
@AssertTrue验证注解的元素值是true

三、@Validated与@Valid区别

@Validated和@Valid都是用于Java Bean验证的注解,它们之间的主要区别在于功能和使用场景:

  1. 分组校验(Groups):
    • @Validated支持校验组,允许开发者根据不同的场景定义一组校验注解,并在运行时指定要执行哪组校验。
    • @Valid不支持校验组,它会触发所有的校验注解。
  2. 嵌套校验:
    • @Validated不支持嵌套校验。
    • @Valid支持嵌套校验。
  3. 使用范围:
    • @Validated 一般用在或者方法参数,但不能用于字段
    • @Valid 可以用在方法参数字段构造器参数上。
  4. 集成方式:
    • @Validated是Spring Framework提供的扩展注解,它在JSR 303/JSR 349/J憝 380的基础上增加了额外的特性,比如对校验组的支持,因此它的使用通常与Spring应用程序结合得更加紧密。
    • @Valid是JSR 303/JSR 349/JSR 380规范的核心注解之一,大多数遵循该规范的框架都支持@Valid。
  5. 默认行为:
    • @Validated允许通过设置ignoreEmpty属性来忽略那些值为空的非空约束注解。
    • @Valid在校验时不会忽略非空的约束注解,即使它们的值是空的也会触发校验。

四、分组校验

在Spring Boot中进行参数分组校验,可以通过定义校验组接口和在注解中指定这些校验组来实现。下面是一个具体的例子:

  1. 首先,定义校验组接口:
public interface DefaultGroup {
}

public interface AdvancedGroup extends DefaultGroup {
}
  1. 然后,在参数实体类中使用这些校验组,通过groups来指定校验注解所属的组。
public class User {

    @NotNull(groups = DefaultGroup.class)
    private String username;

    @NotNull(groups = AdvancedGroup.class)
    private String password;

    // Getter and Setter methods
}
  1. 最后,在控制器方法中使用@Validated注解,并指定校验组。这里只能使用@Validated注解,使用@Valid注解会报错
@RestController
public class UserController {

    @PostMapping("/user")
    public String createUser(@Validated({c}) @RequestBody User user) {
        // 业务逻辑处理
        return "User created successfully";
    }
    
	@PostMapping("/advancedUser")
    public String createAdvancedUser(@Validated({AdvancedGroup.class}) @RequestBody User user) {
        // 业务逻辑处理
        return "User created successfully";
    }
}
在上面的例子中,createUser方法仅应用了DefaultGroup校验组,因此只有username字段会被校验。
而createAdvancedUser方法应用了AdvancedGroup校验组,而在定义AdvancedGroup组的时候继承了DefaultGroup组,
这就相当于同时应用了两个组,所以password和username字段都会被校验。
如果不同的组之间不存在继承关系,又想同时多个组一起校验,
那就使用@Validated({@Validated({AdvancedGroup.class}), AdvancedGroup.class})指定多个组就好。

五、嵌套校验

嵌套校验指的是接收参数的实体里面还嵌套了其他实体对象,需要连同其他的实体对象中的参数一起校验。以下是一个示例来说明如何对嵌套对象进行校验;

  1. 首先,假设我们有两个实体类Parent和Child用来接收参数,其中Parent类包含一个Child类型的属性:
public class Parent {
    @Min(value = 1)
    private Integer id;
    
    private String name;
    
    private Child child;

    // 省略getter和setter方法
}

public class Child {
    @Min(value = 1)
    private Integer id;
    
    @NotNull(message = "Child name cannot be null")
    private String name;

    // 省略getter和setter方法
}
这里Parent 类的id字段做最小值的校验,Child 类的id以及nanme字段都做了校验。
  1. 然后,在控制器方法中对Parent对象及其嵌套的Child对象进行参数校验。其实这里如果只是像上面第一步那样设置接收参数的实体类的话,无论使用@Valid或@Validated注解都无法对Child 对象的属性进行校验,想要达到嵌套校验的效果还需要对第一步接收参数的实体稍作改动
@RestController
public class ParentController {

    @PostMapping("/parent")
    public String createParent(@Valid @RequestBody Parent parent) {
        // 此处可以进行业务逻辑处理,如果校验失败,则会抛出异常
        // ...
        
        return "Parent created successfully";
    }
}
  1. 对第一步定义的Parent 类进行改造,往嵌套的Child对象上添加@Valid注解,只有在需要嵌套校验的对象上添加@Valid注解,才会在接口进行参数校验时,连同子对象一起校验。由于字段属性上只能使用@Valid注解,无法使用@Validated注解,这就是为什么@Validated注解不支持嵌套校验,@Valid注解支持的原因
public class Parent {
    @Min(value = 1)
    private Integer id;
    
    private String name;
    
    @Valid // 实现嵌套校验的关键就在这个注解上
    private Child child;

    // 省略getter和setter方法
}
  1. 第三步修改完后,现在控制器方法中无论使用@Valid或@Validated注解,在调用这个接口的时候,Parent 以及Child 中有注解校验的字段都会进行校验。

六、自定义校验注解

在Spring Boot中创建自定义校验注解涉及以下几个步骤:

  1. 定义校验注解:使用@Constraint注解来定义你的自定义校验注解。你需要指定约束的类型、消息、错误代码和分组。
@Documented
@Constraint(validatedBy = MyConstraintValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConstraint {
    String message() default "自定义校验信息";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  1. 实现校验器:创建一个实现ConstraintValidator接口的类。这个类包含initialize和isValid方法。isValid方法用于实际的校验逻辑。
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {
    
    @Override
    public void initialize(MyConstraint constraintAnnotation) {
        // 初始化代码,如果需要可以从注解中读取配置信息
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        // 这里编写具体的校验逻辑
        if (value == null) {
            return true; // 或者返回false,取决于你希望null值是否通过校验
        }
        // 例如,检查value是否为特定的字符串
        return "expectedValue".equals(value.toString());
    }
}
  1. 在实体或DTO中使用自定义注解:在需要校验的字段上应用你的自定义注解。
public class User {
    
    @MyConstraint(message = "用户名不符合预期")
    private String username;
    
    // 其他字段和方法
}
  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值