1

在编写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;  
    }  
}
  • 测试

image.png

现在一个简单的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"
}

我们可以在代码中增加异常处理,简化、统一处理异常信息,这块优化不在本节内容之中。


指尖改变世界
27 声望6 粉丝