在编写rest接口的时候,一般需要对参数进行校验,常见的校验比如
- NotNull
- NotBlank
- Size
- Min
等等,但是这些注解无法满足我们的需求的时候,该怎么办呢?难道需要在业务代码中进行大量判断吗?非也!我们可以自定义参数校验注解。
普通的rest接口
- 新建一个spring boot工程
package com.yefengyu.validate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ValidateDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ValidateDemoApplication.class, args);
}
}
- 删除test文件夹(我们只是测试一个小功能,无需那么多额外的东西)
- pom文件内容为:主要删除多余的配置,留下下面四个依赖即可
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.yefengyu.validate</groupId>
<artifactId>validate-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>validate-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
</dependencies>
</project>
- 创建一个实体
package com.yefengyu.validate;
import lombok.Data;
@Data
public class Event {
private Long id;
private Integer type;
}
- 编写一个简单 rest 接口
package com.yefengyu.validate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@PostMapping("/test")
public Event test(@RequestBody Event event) {
return event;
}
}
- 测试
现在一个简单的rest接口编写好了,我们现在有了需求, type 只能传递 1、2、3 这三个数值,其它数值传递都视为无效,该怎么办?不能使用业务代码去判断,因为类似这种类型、状态的参数,可以填写的数值本身就不多,我们可以通过自定义校验器来实现。
自定义校验器
首先定义一个注解
package com.yefengyu.validate;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
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)
@Documented
@Constraint(validatedBy = IntegerEnumValidator.class)
public @interface IntegerEnum {
String message() default "invalid number";
int[] values() default {};
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
上面代码很简单,但是多了如下这句:
@Constraint(validatedBy = IntegerEnumValidator.class)
其中 IntegerEnumValidator
类主要是为了校验 IntegerEnum
注解的,代码如下:
package com.yefengyu.validate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IntegerEnumValidator implements ConstraintValidator<IntegerEnum, Integer> {
private IntegerEnum integerEnum;
@Override
public void initialize(IntegerEnum constraintAnnotation) {
this.integerEnum = constraintAnnotation;
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
int[] values = integerEnum.values();
if (values.length == 0) {
return true;
}
for (int v : values) {
if (value == v) {
return true;
}
}
return false;
}
}
此时我们有了自定义的 整型校验器,现在尝试一下:
1.实体增加注解:
package com.yefengyu.validate;
import lombok.Data;
@Data
public class Event {
private Long id;
@IntegerEnum(values = {1, 2, 3}, message = "类型错误")
private Integer type;
}
主要是这句:
@IntegerEnum(values = {1, 2, 3}, message = "类型错误")
2.接口开启注解
package com.yefengyu.validate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class TestController {
@PostMapping("/test")
public Event test(@Valid @RequestBody Event event) {
return event;
}
}
主要是增加了 @Valid
3.测试
{
"id" : 1,
"type" : 11
}
当传入参数 type 不是 1、2、3 的时候,那么结果为:
{
"timestamp": "2019-12-27T08:10:10.569+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"IntegerEnum.event.type",
"IntegerEnum.type",
"IntegerEnum.java.lang.Integer",
"IntegerEnum"
],
"arguments": [
{
"codes": [
"event.type",
"type"
],
"arguments": null,
"defaultMessage": "type",
"code": "type"
},
[
1,
2,
3
]
],
"defaultMessage": "类型错误",
"objectName": "event",
"field": "type",
"rejectedValue": 11,
"bindingFailure": false,
"code": "IntegerEnum"
}
],
"message": "Validation failed for object='event'. Error count: 1",
"path": "/test"
}
我们可以在代码中增加异常处理,简化、统一处理异常信息,这块优化不在本节内容之中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。