@Validated和@Valid

参数校验在 Web 开发中是一项必不可少的工作。它可以帮助开发人员在用户提交表单数据前对数据进行有效性验证,防止恶意攻击,同时也能提高用户体验。在 Spring MVC 中,我们可以使用 @Validated 和 @Valid 注解来进行校验操作。虽然它们都可以实现校验操作,但还是存在一些不同之处。下面我们分别来介绍一下。

注解的定义

@Validated

@Validated 是 Spring 的注解,是对 JSR-303 Bean Validation 的扩展。在spring-context包的org.springframework.validation.annotation路径下, 注解定义如下:

package org.springframework.validation.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<?>[] value() default {};
}

@Valid

@Valid 是 JSR-303/JSR-349 Bean Validation 的注解。它的作用是标注一个 Bean 对象需要校验其属性约束注解。在validation-api包的javax.validation路径下,定义如下:

package javax.validation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}

注解的使用

@Validated

它支持分组校验,同时可以用在方法级别上。在 Spring MVC 中,我们同样可以使用 @Validated 注解进行校验操作。可以在 Controller 层方法中使用 @Validated 注解标记需要被校验的 Bean 对象,如下所示:

@PostMapping("/create")
public String createUser(@RequestBody @Validated User user) {
    // 处理用户数据
}

需要注意的是,使用 @Validated 注解必须借助 Spring 的框架支持。在实际使用中,@Validated 注解一般用于 Controller 层方法参数的校验,可以标注在方法参数、方法上、类上定义验证规则,它的功能比 @Valid 更为强大,支持分组校验、级联校验等更高级的校验功能。

@Valid

在 Spring MVC 中,可以使用 @Valid 注解进行 Bean Validation 校验。具体来说,我们可以在 Controller 层方法中,使用 @Valid 注解标记需要被校验的 Bean 对象,如下所示:

@PostMapping("/create")
public String createUser(@RequestBody @Valid User user) {
    // 处理用户数据
}

需要注意的是,@Valid 只能进行简单的约束条件校验,不能扩展实现逻辑包括分组校验等高级功能。在实际使用中,我们可以结合 Bean Validation 注解,实现复杂的校验逻辑。同样地,我们也可以使用 @Valid 注解对方法中的参数进行校验。

注解的区别

@Validated和@Valid的区别主要体现在3个方面:

分组

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。没有添加分组属性时,默认验证没有分组的验证属性。 

@Valid:作为标准JSR-303规范,还没有吸收分组的功能。

注解地方

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

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上 两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。

嵌套验证

@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解

@Valid进行嵌套验证。@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

示例

@Validated

@Validated 并不仅仅支持简单的 Bean 参数校验,还可以将其嵌套和分组,进行更复杂的校验。下面展示如何使用 @Validated 对嵌套对象、分组、属性进行验证。第一种,嵌套对象:

public class User {

    @NotBlank(message = "用户名不能为空")
    @Size(max = 10, message = "用户名不能超过10个字符")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码必须在6到20个字符之间")
    private String password;

    @Valid
    private Address address;

    // 省略 getter 和 setter 方法
}

public class Address {

    @NotBlank(message = "省份不能为空")
    private String province;

    @NotBlank(message = "城市不能为空")
    private String city;

    @NotBlank(message = "街道不能为空")
    private String street;

    // 省略 getter 和 setter 方法
}

第二种,分组:

public interface UserRegister {
}

public class User {

    @NotBlank(message = "用户名不能为空", groups = {UserRegister.class})
    @Size(max 
10, message = "用户名不能超过10个字符", groups = {UserRegister.class})
    private String username
;

    @NotBlank(message = "密码不能为空", groups = {UserRegister.class})
    @Size(min 
6, max = 20, message = "密码必须在6到20个字符之间", groups = {UserRegister.class})
    private String password
;

    @Valid
    private Address address;

    // 省略 getter 和 setter 方法
}

controller层使用是相同的。

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/save")
    public String createUser(@RequestBody @Validated User user) {
        return "success";
    }
}

@Valid

除不支持分组外,其他使用方式和@Validated类似

public class Address {

    @NotBlank(message = "省份不能为空")
    private String province;

    @NotBlank(message = "城市不能为空")
    private String city;

    @NotBlank(message = "街道不能为空")
    private String street;

    // 省略 getter 和 setter 方法
}

public class User {

    @NotNull(message = "用户 ID 不能为空")
    private Integer userId;

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

    @Valid
    private Address address;

    // getter 和 setter 方法省略
}

@Controller
@RequestMapping("/user")
public class UserController {

    @PostMapping("/create")
    public ResponseEntity createUser(@RequestBody @Valid User user ) {
        // 保存用户信息
        return ResponseEntity.ok("用户创建成功");
    }
}

自定义注解验证

除了使用预定义的注解外,我们还可以自定义一个注解,例如验证用户名是否为手机号码。先定义一个手机号注解 @Phone,然后在 User 对象的 username 字段上使用它。

@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
@Documented
public @interface Phone 
{
    String message() default "请输入正确的手机号码格式";

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

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

public class PhoneValidator implements ConstraintValidator<PhoneString{

    // 校验手机号码格式
    public boolean isValid(String value, ConstraintValidatorContext context) {
        ...
    }
}

public class User {

    @NotNull(message = "用户 ID 不能为空")
    private Integer userId;

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

    @Valid
    private Address address;

    // getter 和 setter 方法省略
}

@Controller
@RequestMapping("/user")
public class UserController {

    @PostMapping("/create")
    public ResponseEntity createUser(@RequestBody @Valid User user ) {
        // 保存用户信息
        return ResponseEntity.ok("用户创建成功");
    }
}

另外

像@NotBlank、@NotNull、@Max、@Min、@Size都是Bean Validation注解,在 validation-api包里,maven坐标如下:

<!-- Bean Validation API -->
<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>2.0.1.Final</version>
</dependency>

使用 Bean Validation API 时,还需要引入一个提供了具体实现的库,校验才会生效。常用的实现有 Hibernate Validator、Apache BVal 等。

<!-- Hibernate Validator -->
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.2.0.Final</version>
</dependency>

原文始发于微信公众号(小新成长之路):@Validated和@Valid

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/238585.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!