Springboot核心:参数校验

数据校验在软件开发和信息系统中扮演着至关重要的角色,它确保了进入系统或业务流程的数据是准确、完整且符合预期的,通过实施有效的数据校验措施,可以为软件应用和信息系统提供多方面的保障和支持。本文介绍Springboot的另一个核心:参数校验。

☆ 简单使用

1. 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 校验方式及注解使用

DTO参数校验

package cn.wideth.validation;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;

@Data
public class UserDTO {

    @Min(value = 1, message = "最小值为1")
    @NotNull(message = "缺少用户id")
    private Long userId;

    @NotBlank(message = "姓名不能为空")
    @Length(min = 2, max = 10,message = "姓名长度为2-10个字符")
    private String userName;

    @NotNull(message = "账号不能为空")
    @Size(min = 8, max = 20, message = "账号长度必须在8到20个字符之间")
    private String account;

    @NotNull(message = "密码不能为空")
    @Length(min = 6, max = 10, message = "密码长度范围在6~10个字符之间")
    @Password
    private String password;

    @NotEmpty(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @NotEmpty(message = "缺少部门信息")
    private List<Depart> depart;
}

校验的使用

// 路径变量
@GetMapping("{userId}")
public String Userdetail(@PathVariable("userId") @NotNull Long userId) {
    // 校验通过,才会执行业务逻辑处理
    return "ok";
}

// 查询参数
@GetMapping("getByAccount")
public String getByAccount(@Length(min = 6, max = 20) @NotNull String  account) {
    // 校验通过,才会执行业务逻辑处理
    return "ok";
}

//DTO校验
@PostMapping("/addUser")
public String addUser(@RequestBody @Validated UserDTO userDTO){
     // 校验通过,才会执行业务逻辑处理
     return "ok";
}

3. 分组校验

public class User {

    @NotNull(groups = Create.class, message = "姓名不能为空")
    private String name;

    @NotEmpty(groups = Create.class, message = "邮箱不能为空")
    @Email(groups = Create.class, message = "邮箱格式不正确")
    private String email;

    @NotNull(groups = Update.class, message = "用户ID不能为空")
    private Long userId;

    ……
}

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

    @PostMapping
    public ResponseEntity<String> createUser(@Validated(Create.class) @RequestBody User user) {
        // 创建用户逻辑
        return ResponseEntity.ok("创建成功");
    }

    @PutMapping("/{id}")
    public ResponseEntity<String> updateUser(@PathVariable Long id, @Validated(Update.class) @RequestBody User user) {
        // 更新用户逻辑
        return ResponseEntity.ok("更新成功");
    }
}

4. 集合校验

如果请求直接传递集合并希望对每一项参数校验,可以直接使用java.util.Collection下的list或者set来接收数据,参数校验并不会生效!通过使用自定义list集合来接收参数:

public class ValidationList<E> implements List<E> {

    @Delegate // @Delegate是lombok注解
    @Valid // 一定要加@Valid注解
    public List<E> list = new ArrayList<>();

    // 一定要记得重写toString方法
    @Override
    public String toString() {
        return list.toString();
    }
}

例如,一次性保存多个用户使用方法:

@PostMapping("/saveUserList")
public Result saveUserList(@RequestBody @Validated(UserDTO.Save.class) ValidationList<UserDTO> userList) {
    // 校验通过,才会执行业务逻辑处理
    return Result.ok();
}

5. 自定义注解校验

如果实际业务场景中,内置的校验器不符合业务需求,可以使用自定义校验器。自定义校验器需要实现ConstraintValidator接口,并在需要校验的字段上使用@Constraint注解进行标记。

例:密码安全校验

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
public @interface Password {
    
    //默认错误消息
    String message() default "密码不符合安全规范";
    
    //分组
    Class<?>[] groups() default {};

    //负载
    Class<? extends Payload>[] payload() default {};
}
注释:
@Constraint(validatedBy = {PasswordValidator.class}):用于指定验证器类;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}):指定@Password 注解可以作用在方法、字段、构造函数、参数以及类型上

实现ConstraintValidator编写校验规则:

public class PasswordValidator implements ConstraintValidator<Password, String> {

    private static final Pattern PATTERN = Pattern.compile("^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$");

    @Override
    public void initialize(Password constraintAnnotation) {
        // 初始化操作
    }

    @Override
    public boolean isValid(String password, ConstraintValidatorContext context) {      
        // 实际的校验逻辑,不为null进行校验
        if (password != null) {
            Matcher matcher = PATTERN.matcher(password);
            return matcher.find();
        }
        return true;
    }
}

使用@Password进行参数校验即可。

6. 全局异常处理

如果校验失败,会抛出MethodArgumentNotValidException或者ConstraintViolationException异常。在实际项目开发中,通常使用统一异常处理来返回一个更友好的提示。

使用@RestControllerAdvice注解可以创建全局异常处理器,在控制器没有处理的异常都会在这里被处理。

@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler {

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Result handleMethodNotValidException(MethodArgumentNotValidException ex) {
        log.error("MethodArgumentNotValidException:{}", e);
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        String msg = sb.toString();
       return Result.fail(BusinessCode.ERROR, msg);
    }

    @ExceptionHandler({ConstraintViolationException.class})
    public Result handleConstraintViolationException(ConstraintViolationException ex) {
        return Result.fail(BusinessCode.ERROR, ex.getMessage());
    }
}

7. 常用注解

  • 通用注解:
@NotNull: 验证注解的元素值不是 null@Null: 验证注解的元素值是 null@AssertTrue: 验证注解的元素值是 true@AssertFalse: 验证注解的元素值是 false@Min(value): 验证注解的元素值大于等于指定的值。
@Max(value): 验证注解的元素值小于等于指定的值。
@Size(max, min): 验证注解的元素值的大小在指定的范围内。
@Digits(integer, fraction): 验证注解的元素值的整数部分和小数部分的数字不超过指定的数值。
  • 字符串验证:
@NotBlank: 验证注解的元素值不为空(不为 null、去掉前后空格后长度大于0)。
@NotEmpty: 验证注解的元素值不为 null 且不为空。
@Email: 验证注解的元素值是一个有效的电子邮件地址。
  • 数值验证:
@Positive: 验证注解的元素值是正数。
@PositiveOrZero: 验证注解的元素值是非负数。
@Negative: 验证注解的元素值是负数。
@NegativeOrZero: 验证注解的元素值是非正数。
  • 日期和时间验证:
@Past: 验证注解的元素值是过去的日期或时间。
@PastOrPresent: 验证注解的元素值是过去或当前的日期或时间。
@Future: 验证注解的元素值是将来的日期或时间。
@FutureOrPresent: 验证注解的元素值是将来或当前的日期或时间。
  • 常见的自定义注解
@Pattern:用于校验字符串是否符合指定的正则表达式。
@DecimalMax:验证数字是否小于等于指定的最大值。
@DecimalMin:验证数字是否大于等于指定的最小值。
@Digits(integer,fraction):被注解元素必须是一个数字,其值必须在指定范围内
@URL:被注解元素必须是合法的 URL

注意事项:

  • @NotBlank和@NotEmpty细节和差异
    @NotBlank注解用于验证字符串非null,且去除首尾空白字符后长度必须大于0
    @NotEmpty注解用于验证集合、数组、Map或者字符串非null,且长度必须大于0

  • @Valid 和 @Validated 的比较
    @Valid:不直接支持分组验证的概念。
    @Validated: 提供了分组校验功能,允许指定一个或多个验证分组,可以根据不同的场景应用不同的验证规则。

参考:文章 FC464782123

你可能感兴趣的:(SpringBoot,spring,boot,后端,java)