Spring boot 参数校验
Posted xiaodujava
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot 参数校验相关的知识,希望对你有一定的参考价值。
文章目录
Spring boot 参数校验
上一节 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 分组
- 创建自定义分组
public interface OrderGroup {}
- 需要校验得实体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;
...
- 请求指定分组校验
@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 | 断言为false | JSR303 |
@AssertTrue | 断言为true | JSR303 |
@DecimalMax | 必须为一个数字,值必须小于或者等于指定的值 | JSR303 |
@DecimalMin | 必须为一个数字,值必须大于或者等于指定的值 | JSR303 |
@Digits | 元素必须是可接受范围内的数字 | JSR303 |
email格式 | JSR303 | |
@Future | 标注在时间类型上,必须是一个未来的时间 | JSR303 |
@FutureOrPresent | 标注在时间类型上,必须是一个未来的时间或者当前 | JSR303 |
@Max | 最大值 | JSR303 |
@Min | 最小值 | JAR303 |
@Negative | 元素必须是严格的负数 | JSR303 |
@NegativeOrZero | 负数或者0 | JSR303 |
@NotBlank | 不能为null和空白 | JSR303 |
@NotEmpty | 不能为null和空字符 | JSR303 |
@NotNull | not null | JSR303 |
@Null | 必须为null | JSR303 |
@Past | 标注在时间类型上,必须为一个过去的时间 | JSR303 |
@PastOrPresent | 标注在时间类型上,为一个过去或者现在的时间 | JSR303 |
@Pattern | 是否匹配正则 | JSR303 |
@Positive | 正数,不包括0 | JSR303 |
@PositiveOrZero | 正数,或者0 | JSR303 |
@Size | 集合大小或者字符串长度限制 | JSR303 |
@Length | 校验字符串长度 | Hibernate 提供 |
@Range | 值得范围 | Hibernate 提供 |
@URL | 合法url | Hibernate 提供 |
自定义校验
通过 @Constraint 指定具体的校验器
示例:
- 自定义注解 @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();
}
}
- 请求中使用
上传文件,会走我们自定义的校验器进行文件后缀的校验
@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 参数校验的主要内容,如果未能解决你的问题,请参考以下文章