01 Bean Validation基础解:快速上手,高效验证

Spring Validation集成了JSR-380 (Bean Validation 2.0)规范,提供了一系列开箱即用的校验注解。

这些注解能够帮助开发者快速实现对数据的基本验证,确保输入数据符合预期。

@Data
public class UserDTO {
    @NotNull(message = "用户ID不能为空")
    private Long id;

    @NotBlank(message = "用户名不能为空")
    @Size(min = 4, max = 20, message = "用户名长度必须在4到20个字符之间")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Min(value = 18, message = "年龄必须大于或等于18")
    @Max(value = 120, message = "年龄必须小于或等于120")
    private Integer age;

    @Past(message = "出生日期必须是过去的日期")
    private LocalDate birthDate;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确")
    private String phoneNumber;
}
@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity createUser(@RequestBody @Valid UserDTO userDTO,
                                             BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            throw new ValidationException(bindingResult);
        }
        return ResponseEntity.ok(userDTO);
    }
}

注解

1. 在UserDTO类中,使用@NotNull@NotBlank@Email等注解对字段进行校验,这些注解能够快速实现对字段的基本验证。

2. 在UserController中,通过@Valid注解对请求参数进行验证,并通过BindingResult获取验证结果。

3.如果校验失败,提交自定义的ValidationException异常,提示用户输入数据不符合要求。

最佳实践

使用有意义的错误消息,保持一致的命名风格,避免在实体类上直接使用验证注解,而是在DTO对象上应用验证规则。

这样可以更好地分离关注点,提高代码的可维护性。

02定制约束验证器:满足特定业务需求,且灵活强大

Spring Validation允许开发者创建自定义约束,满足特定业务规则的验证需求。

通过自定义约束验证器,可以实现复杂的业务逻辑,确保数据符合特定的业务规则。

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
public @interface UniqueUsername {
    String message() default "用户名已存在";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {

    private UserRepository userRepository;

    @Override
    public boolean isValid(String username, ConstraintValidatorContext context) {
        if (username == null) {
            return true;
        }
        return !userRepository.existsByUsername(username);
    }
}
public class UserRegistrationDTO {

    @NotBlank
    @Size(min = 4, max = 20)
    @UniqueUsername
    private String username;

}

注解:

1.定义了一个@UniqueUsername注解,用于验证用户名是否唯一。

2.实现了ConstraintValidator接口,重写了isValid方法,通过调用userRepository.existsByUsername方法来判断用户名是否已存在。

3.在UserRegistrationDTO类中,使用@UniqueUsername注释解对username字段进行校验。

使用: 场景验证业务特定规则,如唯一性约束、密码复杂度、信用卡格式等。

通过自定义约束验证器,可以灵活地实现各种复杂的业务逻辑校验。

03分组验证:根据不同场景,灵活切换验证规则

分组验证允许根据不同场景应用不同的验证规则,例如创建和更新操作可能需要不同的验证逻辑。

通过分组验证,可以灵活切换验证规则,满足不同场景下的校验需求。

public interface ValidationGroups {
    interface Create {}
    interface Update {}
}
@Data
public class ProductDTO {

    @Null(groups = ValidationGroups.Create.class, message = "创建产品时ID必须为空")
    @NotNull(groups = ValidationGroups.Update.class, message = "更新产品时ID不能为空")
    private Long id;

    @NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})
    private String name;

    @PositiveOrZero(groups = ValidationGroups.Create.class)
    @Positive(groups = ValidationGroups.Update.class)
    private BigDecimal price;
}
@RestController
@RequestMapping("/api/products")
public class ProductController {

    @PostMapping
    public ResponseEntity createProduct(
            @RequestBody @Validated(ValidationGroups.Create.class) ProductDTO productDTO) {
        return ResponseEntity.ok(productDTO);
    }

    @PutMapping("/{id}")
    public ResponseEntity<ProductDTO> updateProduct(
            @PathVariable Long id,
            @RequestBody @Validated(ValidationGroups.Update.class) ProductDTO productDTO) {
        return ResponseEntity.ok(productDTO);
    }
}

注解:

1.定义了两个分组接口CreateUpdate,分别用于创建和更新操作的验证。

  1. 2. 在ProductDTO类中,通过groups属性指定不同字段在不同分组下的验证规则。

  2. 3. 在ProductController中,通过@Validated注解指定了使用不同接口方法的验证包。

提示:注意使用@Validated注解而不是@Valid,因为只有之前支持分组验证。

通过分组验证,可以灵活切换验证规则,满足不同场景下的校验需求。

04诉求验证:深入复杂对象结构,确保数据缺陷

请求验证允许验证复杂对象结构中的请求对象。

通过在需要级联验证的字段上添加@Valid注解,可以确保验证深入到请求对象中,从而保证整个对象结构的数据完整性。

@Data
public class OrderDTO {

    @NotNull
    private Long id;

    @NotNull
    @Valid
    private CustomerDTO customer;

    @NotEmpty
    @Valid
    private List<OrderItemDTO> items;
}

@Data
public class CustomerDTO {

    @NotNull
    private Long id;

    @NotBlank
    private String name;

    @Email
    private String email;

    @Valid
    private AddressDTO address;
}

注解:

1.在OrderDTO类中,customer和items字段使用了@Valid注解,表示对这些请求对象进行级联验证。

2.在CustomerDTO类中,address字段也使用了@Valid注解,确保验证能够深入到层次的对象结构中。

关键点:

在需要级联验证的字段上添加@Valid注释,确保验证深入到请求对象中。

通过请求验证,可以确保复杂对象结构中的每个字段都符合校验规则,从而保证数据的完整性。

05方法级别验证:服务层验证,增强代码健壮性

Spring Validation不仅可以用于控制器参数,还可以评估服务层的方法。

通过方法级别验证,可以确保服务层方法接收到的参数和返回的结果符合预期,增强代码的健壮性。

@Configuration
@EnableMethodValidation
public class ValidationConfig {

}
@Service
public class UserService {

    @Validated
    public User createUser(@Valid UserDTO userDTO) {
        return new User();
    }

    @NotNull
    public User findById(@Min(1) Long id) {
        return new User();
    }

    @Validated(ValidationGroups.Update.class)
    public void updateUser(@Valid UserDTO userDTO) {
    }
}

注解:

1.在ValidationConfig类中,通过@EnableMethodValidation注解启用了方法级别验证。

2.在UserService类中,通过@Validated注解对方法参数和返回值进行校验。

3.在findById方法中,使用@Min注解对id参数进行校验,确保其值大于或等于1。

应用场景:

确保服务层方法接收到的参数和返回的结果符合预期,增强代码的健壮性。

通过方法级别验证,可以将校验逻辑从业务逻辑中分离出来,提高代码的可维护性和区分性。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐