SpringBoot:SpringBoot 接口参数校验几种实用技巧

一、前言

  在 Spring Boot 中,接口参数校验是一个常见的需求,用于确保接收到的数据符合预期的格式和规则。Spring Boot 提供了多种参数校验的方式,包括使用 JSR 303/380 规范的注解(如 Hibernate Validator)、自定义校验注解、AOP 拦截等。

二、JSR 是什么?

  JSR 是 Java Specification Requests 的缩写,意思是 Java 规范提案。是指向 JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交 JSR,以向 Java 平台增添新的 API 和服务。JSR 已成为 Java 界的一个重要标准。JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

  在Spring中提供了SpringValidation验证框架对参数的验证机制提供了@Validated(Spring’sJSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),结合BindingResult对象可以直接获取错误信息

三、@Valid 与 @Validated区别?

1. 用法位置:
  @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上,支持嵌套检测。它通常与@RequestBody一起使用,以验证传入JSON或XML等格式的请求体。

  @Validated:可以用在类型、方法和方法参数上,但不能直接用在成员属性(字段)上。它支持分组校验,允许你定义不同的校验组并在需要时应用它们。

2. 嵌套验证:
  @Valid 支持嵌套验证,即如果待验证的类中包含其他需要验证的类,那么@Valid也会验证这些嵌套类的属性。

  @Validated 默认情况下不支持嵌套验证,但可以通过配置来支持。

3. 分组校验:
  @Validated 支持分组校验,允许你定义不同的校验组并在需要时应用它们。这对于在不同场景下应用不同的校验规则非常有用。

  @Valid 不直接支持分组校验,但可以通过其他方式(如自定义校验器)实现类似的功能。

4. 与Spring框架的集成:
  @Valid 是JSR 303/380规范的一部分,是标准的Java Bean验证(Bean Validation)API的注解。它可以在任何支持Bean Validation的环境中使用,包括Spring。

  @Validated 是Spring框架提供的注解,提供了对JSR 303/380规范的扩展和增强。它除了支持标准的Bean Validation功能外,还提供了额外的功能,如分组校验和自定义校验器。

四、参数校验技巧及代码案例

引入依赖

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

准备一个入参实体类

package com.example.yddemo.validate;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;

public class User {

    @Min(value = 10, message = "ID不能小于 10", groups = V1.class)
    @Min(value = 20, message = "ID不能小于 20", groups = V2.class)
    private Long id;

    @NotEmpty(message = "姓名必需填写")
    private String name;

    private String phone;
	
	// 分组校验使用
    public static interface V1 {}

    public static interface V2 {}

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

全局异常信息返回

package com.example.yddemo.validate;

import com.alibaba.fastjson.JSON;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity<?> handleValidationExceptions(Exception ex) {
        String message = "";
        BindingResult bindingResult = null;
        // 提取错误信息并返回给前端
        // ...
        if (ex instanceof ConstraintViolationException) {
            ConstraintViolationException e = (ConstraintViolationException) ex;
            message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
        } else if (ex instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
            Optional<List<String>> list = Optional.of(bindingResult.getAllErrors().stream().map(err -> err.getDefaultMessage()).collect(Collectors.toList()));
            message = JSON.toJSONString(list.get());
        }

        return ResponseEntity.badRequest().body(message);
    }

}

1. 单个参数校验

@RestController
@Validated
public class TestValidateController1 {

    @GetMapping("/user/validate/one")
    public String userValidateOne(@NotEmpty(message = "参数 Id 不能为空") String id) {
        // 业务逻辑
        return "success" + id;
    }
}    

在这里插入图片描述

2. 简单实体类参数校验

package com.example.yddemo.validate;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class TestValidateController {

    @GetMapping("/user/validate1")
    public User userValidate1(@Valid @RequestBody User user) {
        // 业务逻辑
        return user;
    }
}

在这里插入图片描述

3. 参数校验分组
  当需要根据不同的业务场景执行不同的校验规则时,可以使用分组校验。

@GetMapping("/user/validate/v2")
    public User userValidateV2(@Validated(User.V2.class) @RequestBody User user) {
        // 业务逻辑
        return user;
    }


    @GetMapping("/user/validate/v1")
    public User userValidateV1(@Validated(User.V1.class) @RequestBody User user) {
        // 业务逻辑
        return user;
    }

在这里插入图片描述
在这里插入图片描述
4. 嵌套参数校验
  实际的工作中往往参数对象比这复杂的多,User 对象中可能还嵌套有其他的对象,这个其他的对象也可能需要参数的校验

@Valid
    private Order order;


package com.example.yddemo.validate;

import javax.validation.constraints.NotEmpty;

public class Order {

    @NotEmpty(message = "编码不能为空")
    private String code;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

在这里插入图片描述

5. 自定义注解校验
  当 JSR 303/380 提供的注解不满足需求时,可以自定义校验注解。

构造MobileValidator 验证器

package com.example.yddemo.validate;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MobileValidator implements ConstraintValidator<MobileCheck, String> {
    // 处理细节

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return false;
    }

    @Override
    public void initialize(MobileCheck constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }
}

注解

package com.example.yddemo.validate;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.Pattern;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MobileValidator.class)
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确")
public @interface MobileCheck {

    String message() default "手机号码格式不正确";

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

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

}

在实体类DTO中使用

 @MobileCheck
    private String phone;

在这里插入图片描述
总结:以上是使用注解@Valid 与@Validated 实现SpringBoot 接口参数校验几种实用技巧,也可以使用AOP 拦截实现参数校验,下一篇我们在详细介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值