zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【开发日记】SpringBoot做参数校验

SpringBoot开发 参数 校验 日记
2023-06-13 09:15:41 时间

目录:

1、前言 2、加入依赖 3、创建VO实体类 4、创建接口 5、创建全局异常处理器 6、添加效验注解 7、分组 8、优化参数效验 9、@Validated或@Valid区别 10、效果

1、前言

这里的参数效验指的是在Web接口中接收参数时对参数的合法性进行效验;正常情况的做法是在接收到参数时,在方法体中对参数进行核验;这样做的代码整洁性太差、代码侵入性太强;这里推荐一个利用SpringBoot中推荐的注解方式进行参数效验。

2、加入依赖

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

3、创建VO实体类

@Data
public class Parameter implements Serializable {
    String scene;
    ArrayList<Point> path;
    Double speed;
    Integer state;
    JSONObject inputValues;
}

这个类用于接收客户端的请求参数;使用实体类接收参数时实体类需要有Getter、Setter方法,我这里用到Lombok下的@Data注解自动生成这些方法,所以就没有加Getter、Setter方法。

4、创建接口

@PostMapping("")
public RequestResult app(@Validated @RequestBody Parameter parameter) {
    RequestResult requestResult = new RequestResult();
    requestResult.appendData("result", app.start(parameter));
    return requestResult;
}

需要在接收参数的实体类上加入@Validated@Valid注解,否则参数效验不会生效,这两个注解的区别后面说明。

相信能看到这里的小伙伴使用SpringMVC创建接口应该问题不大,所以这里就不展示接口类了,重点在使用实体类接收参数时如何使用注解对参数进行效验。

5、创建全局异常处理器

@RestControllerAdvice
public class ParameterExceptionHandler {
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public RequestResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        RequestResult requestResult = new RequestResult();
        requestResult.setCode(400);
        requestResult.setMessage(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
        requestResult.appendData("url",request.getRequestURI());
        return requestResult;
    }
}

当参数效验失败,会抛出MethodArgumentNotValidException异常,这个类的作用就是捕获这个异常,然后将这个异常翻译成用户能看懂的样子。

6、添加效验注解

用于效验的注解可用于方法参数中,也可以用于实体类中;如果是实体类中使用的话,需要在方法参数实体类前加入@Validated注解;如果实体类中有嵌套其他的实体类,也需要参数效验,则使用@Valid注解标识,@Validated@Valid注解区别会在后面说明。

@NotEmpty

表示不能为null,也不能为空字符串,当类型为集合时集合不能为空,但是字符串可以是" "(注意引号之间有一个空格)。

@NotEmpty(message = "季节不能为空")
String scene;

@NotNull

表示不可以为null,但可以是空字符串;

@NotNull(message = "季节不能为空")
String scene;

@NotBlank

用于字符串,表示不能为null,也不能为空字符串,空格字符串也不行。

@Length

用于字符串上,限制字符串长度。

@Length(max = 1,min = 1,message = "用于表示季节的字符为A(夏季)、B(冬季)")
String scene;

@Size

用于字符串、数组和集合上,也是限制长度。

@Size(min = 2, message = "表示路线的坐标点应该不少于2个")
ArrayList<Point> path;

@Min@Max

表示最小值和最大值,用于字符串或数值上,如果是字符串则转换为BigDecimal再进行比较。

@Min(value = 10,message = "速度应大于等于10千米每小时")
@Max(value = 20,message = "速度应小于等于20千米每小时")
Double speed;

@Range

表示取值范围,与@Min@Max组合使用类似。

@Range(min = 1, max = 3, message = "状态应使用1-正常、2-加速、3-慢速")
Integer state;

@Email

效验邮箱格式。

@Email(message = "格式不符合规范")
String mail;

@URL

表示该参数值必须是一个URL。

@URL(message = "格式不符合规范")
String url;

@Pattern

表示该参数值必须符合这个正则表达式。

@Pattern(regexp = "^[A-Z]+$",message = "格式不符合规范")
String scene;

7、分组

使用@Validated注解可设置参数效验分组;示例如下:

① 创建两个分组

public interface Autumn {
}

public interface Spring {
}

② 控制器接口

表示当前是Spring分组;

@PostMapping("")
public RequestResult app(@Validated({Spring.class}) @RequestBody Parameter parameter) {
    RequestResult requestResult = new RequestResult();
    requestResult.appendData("result", app.start(parameter));
    return requestResult;
}

③标识参数分组

表示只有当前分组是Autumn时才会生效。

@Size(min = 2, message = "表示路线的坐标点应该不少于2个",groups = {Autumn.class})
ArrayList<Point> path;

8、优化参数效验

如上的效验过程中,会效验所有的参数,然后把不合格的全部返回给客户端。

通过如下配置可以做到当有一个参数效验不通过时即返回,不用效验所有参数,增加效率。

@Configuration
public class AppConfig {
    @Bean
    public Validator validator(AutowireCapableBeanFactory beanFactory) {
        try (ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure().failFast(true)
                .constraintValidatorFactory(new SpringConstraintValidatorFactory(beanFactory))
                .buildValidatorFactory()) {
            return validatorFactory.getValidator();
        }
    }
}

9、@Validated@Valid区别

①用法

@Validated注解可被用于方法参数上;无法用于成员属性上;

@Valid注解可被用于方法构造方法、参数和成员属性上;

②分组

@Validated注解提供分组用法,可根据分组情况提供不同的参数效验规则;

@Valid注解不提供;

10、效果

参数效验未通过后的效果如下:

{
    "code": 400,
    "message": "表示路线的坐标点应该不少于2个",
    "data": {
        "url": "/test"
    }
}