Spring Boot集成validation实现参数校验功能

1.什么是Bean Validation?

Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。java 在2009年的 JAVAEE 6 中发布了 JSR303以及javax下的validation包内容。这项工作的主要目标是为java应用程序开发人员提供 基于java对象的 约束(constraints)声明 和 对约束的验证工具(validator),以及约束元数据存储库和查询API。但是该内容并没有具体的实现, Hibernate-Validator框架 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。  

常用的Validation注解

一些最常见的验证注解如下:

  • @NotNull: 标记字段不能为 null
  • @NotEmpty: 标记集合字段不为空(至少要有一个元素)
  • @NotBlank: 标记字段串字段不能是空字符串(即它必须至少有一个字符)
  • @Min / @Max: 标记数字类型字段必须大于/小于指定的值
  • @Pattern: 标记字符串字段必须匹配指定的正则表达式
  • @Email: 标记字符串字段必须是有效的电子邮件地址

2.代码工程

实验目标:校验controller传递的参数

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>validtion</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.16</version>
        </dependency>

    </dependencies>
</project>

controller

Spring 自动将传入的 JSON 映射到 Java 对象参数上。 现在,我们要校验传入的 Java 对象是否满足我们预先定义的约束条件。 为了校验传入 HTTP 请求的请求实体,我们在 REST 控制器中使用 @Valid 注解对请求实体进行标记:

package com.et.validation.controller;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.et.validation.entity.UserInfoReq;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class HelloWorldController {
    @RequestMapping("/hello")
    public Map<String, Object> showHelloWorld(@RequestBody @Validated UserInfoReq req){
        Map<String, Object> map = new HashMap<>();
        map.put("msg", JSONUtil.toJsonStr(req));
        return map;
    }
}

enttiy

我们有一个 int类型 字段,它的值必须介于 1 和 10200之间,如@Min 和@Max 注解所定义的那样。 我们还有一个 String 类型字段,它必须是一个 IP 地址,正如@Pattern 注解中的正则表达式所定义的那样(正则表达式实际上仍然允许大于 255 的无效 IP 地址,但在我们创建自定义验证器时,我们将在本教程的后面修复这个BUG)。

package com.et.validation.entity;

import com.et.validation.validate.IpAddress;
import lombok.Data;
import lombok.ToString;

import javax.validation.constraints.*;

@Data
@ToString
public class UserInfoReq {

    @NotNull(message = "id不能为null")
    private Long id;

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

    @NotNull(message = "age不能为null")
    @Min(value = 1, message = "年龄不符合要求")
    @Max(value = 200, message = "年龄不符合要求")
    private Integer age;
    @Email(message = "邮箱不符合规范")
    private String email;
    @IpAddress(message = "ip不符合规范")
    //@Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$") 
    private String ip;
}

自定义error捕获

当校验失败时,我们通常希望向客户端返回一条有意义的错误消息。 为了使客户端能够显示有用的错误消息,我们应该返回一个统一的数据结构,其中包含每个校验失败的错误消息。 我们在这里所做的只是从异常中读取有关校验失败信息并将它们转换到我们的 ValidationErrorResponse 数据结构中。 请注意@ControllerAdvice 注解,它使得上述类型异常的异常处理机制对所有Controller全局可用。

package com.et.validation.error;

import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

@ControllerAdvice
class ErrorHandlingControllerAdvice {

  @ExceptionHandler(ConstraintViolationException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ResponseBody
  ValidationErrorResponse onConstraintValidationException(
      ConstraintViolationException e) {
    ValidationErrorResponse error = new ValidationErrorResponse();
    for (ConstraintViolation violation : e.getConstraintViolations()) {
      error.getViolations().add(
        new Violation(violation.getPropertyPath().toString(), violation.getMessage()));
    }
    return error;
  }

  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ResponseBody
  ValidationErrorResponse onMethodArgumentNotValidException(
      MethodArgumentNotValidException e) {
    ValidationErrorResponse error = new ValidationErrorResponse();
    for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
      error.getViolations().add(
        new Violation(fieldError.getField(), fieldError.getDefaultMessage()));
    }
    return error;
  }

}
package com.et.validation.error;

import lombok.Data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
public class ValidationErrorResponse implements Serializable {

  private List<Violation> violations = new ArrayList<>();
}


package com.et.validation.error;

import lombok.Data;

import java.io.Serializable;
@Data
public class Violation implements Serializable {

  private final String fieldName;

  private final String message;


  public Violation(String fieldName, String message) {
    this.fieldName = fieldName;
    this.message = message;
  }
}

自定义validator

如果官方提供可用的校验注解不能满足我们的需要,我们自己可以定义一个自定义校验器。 自定义校验注解需要包含以下要素:

  • 参数message, 指定 ValidationMessages.properties 文件中的属性键,用于在校验失败时解析提示消息,
  • 参数 groups, 允许定义在何种情况下触发此校验(稍后我们将讨分组校验)
  • 参数payload, 允许定义要通过此校验传递的Payload(因为这是一个很少使用的功能,我们不会在本教程中介绍它)
  • 一个 @Constraint 注解, 指定实现 ConstraintValidator 接口的校验逻辑类。

校验器的实现如下所示:

package com.et.validation.validate;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = IpAddressValidator.class)
@Documented
public @interface IpAddress {

  String message() default "{IpAddress.invalid}";

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

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

}
package com.et.validation.validate;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class IpAddressValidator implements ConstraintValidator<IpAddress, String> {

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    Pattern pattern =
      Pattern.compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
    Matcher matcher = pattern.matcher(value);
    try {
      if (!matcher.matches()) {
        return false;
      } else {
        for (int i = 1; i <= 4; i++) {
          int octet = Integer.valueOf(matcher.group(i));
          if (octet > 255) {
            return false;
          }
        }
        return true;
      }
    } catch (Exception e) {
      return false;
    }
  }
}

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

3.测试

启动spring boot应用

测试参数校验接口

访问http://127.0.0.1:8088/hello,返回结果如下

validate

 

4.引用

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot,可以使用Spring Boot Validation来对参数名称进行校验。通过引入spring-boot-starter-validation依赖,可以使用Spring Validator对参数进行校验Spring Validator是对Hibernate Validator的进一步封装,同时支持Spring MVC的自动校验。你可以在Spring官方文档找到更多关于Spring Boot Validation的详细信息\[2\]。 在使用Spring Boot Validation时,可以使用@Validated注解来标记需要校验的类型、方法和方法参数。@Validated注解属于org.springframework.validation.annotation包,是Spring校验机制之一。它具有分组校验功能,可以用于类型、方法和方法参数上,但不能用于成员属性(field)\[3\]。 通过使用Spring Boot Validation,你可以简化参数校验的代码,提高代码的可读性和美观性。 #### 引用[.reference_title] - *1* *3* [Spring Boot参数校验](https://blog.csdn.net/qq1929892209/article/details/126133350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring Boot 参数校验校验工具类](https://blog.csdn.net/WEDUEST/article/details/121594610)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值