Spring boot 参数校验

Posted xiaodujava

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot 参数校验相关的知识,希望对你有一定的参考价值。

Spring boot 参数校验

上一节 Spring boot crud 和 swagger使用

Spring boot crud 和 swagger使用

源码

源码地址


springboot web模板已经自动集成了 hibernate-validator; 所以我们引入 spring-boot-starter-web 即可使用;

简介

java 参数校验;是遵循JSR-303 规范,对这种规范实现支持最好的就是 Hibernate Validator; spring 底层也是使用的hibernate validator。简化了 我们在硬编码对参数和bean的校验。

基本数据类型参数校验(包括string,以及基本数据类型的包装类)

请求只接受一个基本类型参数的时候(包括string)不是一个bean;
我们需要在类上加@Validated 然后在方法参数上加上对应的校验注解

我们以String 类型参数为例

使用@Length对string 长度的校验

// 类上加注解 @Validated
@Validated
public class ProductController {

// 参数中使用校JSR303  bean校验注解
@GetMapping("product")
public Product getProduct(@Length(max = 32, min = 32) @RequestParam String id) throws Exception {
...
}
}

通过swagger 调用; 上面我们限制了 id的长度为32, 我们输入一个错误的长度
在这里插入图片描述
我们输入长度为1 的id; 校验生效 提示 长度需要在32到32之间;这是框架自动给我们返回的
当然我们也可以指定message来设置消息;例如:
@Length(max = 32, min = 32, message = “id长度必须为32位”),其他的基本类型使用基本一样;
例如 int , long 值可以使用 @Max @Min 等注解修饰;具体参考常用注解, 不同类型使用不同的校验方式。

实体参数校验

对参数bean进行校验。我们在实体类上使用注解
例如 :
- @NotBlank 不能为null以及空白字符
- @Max 最大值
- @Min 最小值

public class Product {

    @ApiModelProperty(required = true, value = "商品名称")
    @NotBlank
    @Length(min = 1, max = 500)
    private String name;

    @ApiModelProperty(required = true, value = "商品id")
    @NotBlank
    @Length(max = 32, min = 32)
    private String id;

    @ApiModelProperty(required = true, value = "商品价格,以分为单位", dataType = "java.lang.Long", example = "1000")
    @Max(Integer.MAX_VALUE)
    @Min(0)
    private Long price;

上面对实体类Product 进行了name的长度和不能为空白的校验以及id长度限制,price最大值最小值

请求方法添加@Validated 开启校验

  @ApiOperation(value = "添加商品信息", httpMethod = "POST")
    @PostMapping("product")
    public String addProduct(@Validated Product product) throws Exception {
        return product.getId();
    }

使用swagger 调用; 返回400,并且是一组json格式的校验信息很不友好;下个章节对全局异常的处理,设置返回我们自己封装的信息。更加友好简短的返回体

{
  "timestamp": "2021-03-31T08:47:28.158+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "Length.product.id",
        "Length.id",
        "Length.java.lang.String",
        "Length"
      ],
      "arguments": [
        {
          "codes": [
            "product.id",
            "id"
          ],
          "arguments": null,
          "defaultMessage": "id",
          "code": "id"
        },
        32,
        32
      ],
      "defaultMessage": "长度需要在32和32之间",
      "objectName": "product",
      "field": "id",
      "rejectedValue": "111",
      "bindingFailure": false,
      "code": "Length"
    }
  ],
  "message": "Validation failed for object='product'. Error count: 1",
  "path": "/validator/product"
}

分组校验

@Validated 是spring 提供的注解,支持分组校验;通过groups 属性进行分组

 例如:
 @Length(min = 32, max = 32, message = "orderId 长度为32", groups = OrderGroup.class)
 通过 属性groups 进行指定是那个组,不指定默认spring提供了javax.validation.groups.Default 分组
  1. 创建自定义分组
    public interface OrderGroup {}
  1. 需要校验得实体bean上进行分组指定 orderGroup
@ApiModel(description = "订单信息描述")
public class OrderEntity {

    @ApiModelProperty(required = true, value = "订单编号")
    @Length(min = 32, max = 32, message = "orderId 长度为32", groups = OrderGroup.class)
    @NotBlank(message = "orderId 必填")
    private String orderId;
...
  1. 请求指定分组校验
   @ApiOperation(value = "添加订单", httpMethod = "POST", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @PostMapping("order")
    public String addOrder(@Validated({Default.class, OrderEntity.OrderGroup.class}) @RequestBody OrderEntity orderEntity) throws Exception {
        return orderEntity.getOrderId();
    }

通过@validated value 可以指定多个分组同时生效,Default.class 为默认分组,若不指定则默认Default.class分组。

示例代码中 属性orderId 同时使用了@Length 和 @NotBlank校验注解; 并且同时指定了分组default 和orderGroup,
所以@Length 和 @NotBlank 都会生效;若不指定分组则默认 @NotBlank 生效;也可以指定多个同时生效。

嵌套校验

使用@Valid 进行实体的嵌套校验。
@Valid 是java自带的校验注解;支持属性嵌套参数校验; 主要是 对实体bean的级联校验;

在实体的属性上添加@Valid

@ApiModel(description = "订单信息描述")
public class OrderEntity {
...
    @ApiModelProperty(required = true, value = "订单详情")
    @NotNull
    @Valid
    private OrderInfo orderInfo;
    ...

OrderInfo 是 OrderEntity 的一个属性; OrderInfo 的属性也添加校验注解,则 对属性 OrderEntity.orderInfo.orderSorce 的校验也生效

   @ApiModel(description = "订单扩展详情")
    public class OrderInfo {

        @ApiModelProperty(value = "订单来源", required = true)
        @Length(max = 32, min = 1)
        @NotBlank
        private String orderSource;

常用校验注解

注解说明备注
@AssertFalse断言为falseJSR303
@AssertTrue断言为trueJSR303
@DecimalMax必须为一个数字,值必须小于或者等于指定的值JSR303
@DecimalMin必须为一个数字,值必须大于或者等于指定的值JSR303
@Digits元素必须是可接受范围内的数字JSR303
@Emailemail格式JSR303
@Future标注在时间类型上,必须是一个未来的时间JSR303
@FutureOrPresent标注在时间类型上,必须是一个未来的时间或者当前JSR303
@Max最大值JSR303
@Min最小值JAR303
@Negative元素必须是严格的负数JSR303
@NegativeOrZero负数或者0JSR303
@NotBlank不能为null和空白JSR303
@NotEmpty不能为null和空字符JSR303
@NotNullnot nullJSR303
@Null必须为nullJSR303
@Past标注在时间类型上,必须为一个过去的时间JSR303
@PastOrPresent标注在时间类型上,为一个过去或者现在的时间JSR303
@Pattern是否匹配正则JSR303
@Positive正数,不包括0JSR303
@PositiveOrZero正数,或者0JSR303
@Size集合大小或者字符串长度限制JSR303
@Length校验字符串长度Hibernate 提供
@Range值得范围Hibernate 提供
@URL合法urlHibernate 提供

自定义校验

通过 @Constraint 指定具体的校验器
示例:

  1. 自定义注解 @FileSufixx 校验文件后缀
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Documented
@Repeatable(value = FileSufixx.List.class)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidatedFileSufixx.class)
public @interface FileSufixx {

    String value() default "docx";

    String message() default "上传的文件类型不对,请检查";

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

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

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @interface List {

        FileSufixx[] value();
    }
}

通过@Constraint(validatedBy = ValidatedFileSufixx.class) 指定校验器
2. 编写校验器
在方法isValid 进行文件后缀名称的判断

public class ValidatedFileSufixx implements ConstraintValidator<FileSufixx, MultipartFile> {

    private String fileSufixx;

    @Override
    public boolean isValid(MultipartFile value, ConstraintValidatorContext context) {
        if (fileSufixx == null || fileSufixx.trim().equals("")) return true;

        String originalFilename = value.getOriginalFilename();
        if (originalFilename.contains(".")) {
            if (originalFilename.substring(originalFilename.lastIndexOf(".") + 1).equals(fileSufixx)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void initialize(FileSufixx constraintAnnotation) {
        fileSufixx = constraintAnnotation.value();
    }
}
  1. 请求中使用
    上传文件,会走我们自定义的校验器进行文件后缀的校验
    @ApiOperation(value = "上传", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public void upload(@RequestPart("file") @FileSufixx MultipartFile file) {
        System.out.println(file.getOriginalFilename());
    }

下一节 Spring boot 异常配置

Spring boot 异常配置

以上是关于Spring boot 参数校验的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot参数校验以及分组校验的使用

Spring Boot 参数校验

手把手写一个基于Spring Boot框架下的参数校验组件

Spring-boot参数校验:基本用法

手把手写一个基于Spring Boot框架下的参数校验组件(JSR-303)

Spring boot 参数校验