当前位置: 首页 > article >正文

Java中JSR303校验

1、简介

jsr 是 Java Specification Requests 的缩写,意思是java的请求规范。周志明老师的书上还着重介绍过jsr292(jvm多语言支持包括Kotlin,Clojure,JRuby,Scala等)。

JSR303着重参数校验功能,点开javax.validation.constraints,可以看到已经封装好的注解有这些:

使用jsr303规范很简单,第一步在实体类相应字段上标注校验注解,比如@Email或者标注自定义校验注解@Pattern(regexp=”“)自定义正则表达式来处理;第二步是使用校验,只需要在@RequestBody之前加上@Valid注解即表明开启校验。

2、分组校验

更复杂的场景,我们可以分组校验:

​ 1)、 @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})​ 给校验注解标注什么情况需要进行校验,比如增加不校验修改时校验。这里group里传入的是个接口。​ 2)、开启分组校验要使用spring实现的注解@Validated({AddGroup.class})​ 3)、默认没有指定分组的校验注解比如@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效,也就是说Validated后加了分组那么不加分组的校验注解就会失效;

细节如下:

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
     * 介绍
     */
    private String descript;
    /**
     * 显示状态[0-不显示;1-显示]
     */
//  @Pattern()
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(values = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
     * 检索首字母
     */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;
}

controller层内容:

public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand)
3、自定义校验注解

还有自定义校验,可以编写一个自定义的校验注解,然后编写一个自定义的校验器 ConstraintValidator,然后两者关联。

也就是说@Pattern()正则不能满足校验的情况,可以使用自定义校验注解。

比如对showStatus做自定义校验,规定只能是整数0或1。

需要先按照规范自定义一个注解@ListValue()

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.flitsneak.common.valid.ListValue.message}";

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

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

    int[] values() default {};
}

注解增加属性int[],提示信息仿照规范自定义common模块resource目录下新建

ValidationMessages.properties文件

com.flitsneak.common.valid.ListValue.message=必须提交指定的值

validatedBy后传入我们自定义的校验器,注解作为参数通过自定义校验器ListValueConstraintValidator对参数进行校验。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author FlitSneak
 * @Date 2021/6/24
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    private final Set<Integer> set = new HashSet<>();
    /**
     * 初始化方法
     * 参数:自定义注解的详细信息
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] values = constraintAnnotation.values();
        for (int val : values) {
            set.add(val);
        }

    }

    /**
     * 判断是否校验成功
     *
     * @param value   需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

就是将注解的值放大set集合之中,然后对字段之判断是否在set集合之中。

4、统一拦截处理

校验响应的结果可以用BindingResult来接收处理返回,但是相当麻烦,推荐做统一拦截处理。

BingdingResult处理方式:

public R save(@Valid @RequestBody BrandEntity brand, BindingResult bindingResult){
        if (bindingResult.hasErrors()){
            Map<String,String> map = new HashMap<>();
            //获取校验错误结果
            bindingResult.getFieldErrors().forEach(i->{
                //获取到错误提示
                String message = i.getField();
                //获取出错的字段
                String field = i.getField();
                map.put(field,message);
            });
            return R.error(400,"校验错误").put("data",map);
        }else {

		brandService.save(brand);

        return R.ok();}
    }

改为ControllerAdvice统一拦截处理,

@Slf4j
@RestControllerAdvice(basePackages = "com.flitsneak.mall.product.controller")
public class MallControllerAdvice {
    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data",errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){

        log.error("错误:",throwable);
        return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(),BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
    }
}

指定拦截的是MethodArgumentNotValidException异常,异常对象e里面可以获取BindingResult,处理方式一样。

补充

前端和后端都应该对参数做检验屏蔽非法请求,jsr303很多企业并没有应用,仍然是 使用CollectionUtil或者StringUtil进行处理。

我们项目用的springboot版本是2.3.x,而2.3.x以上剥离了jsr303,所以要使用注解校验,需要导入一下两个包:

        <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

spring-boot-starter-validation下的version字段和springboot版本对应,本项目springboot版本是2.3.4.RELEASE


http://www.kler.cn/news/359372.html

相关文章:

  • Flux.merge 使用说明书
  • 基于STM32的工厂安防巡检机器人设计流程实现自主识别检测、机器人自主行驶、环境监控和数据采集
  • 传智杯 第六届—第二场—D
  • MATLAB支持的字体
  • 人工智能发展:一场从“被教导”到“自我成长”的奇妙冒险
  • MySQL—CRUD—进阶—(二) (ಥ_ಥ)
  • 【设计模式】深入理解Python中的过滤器模式(Filter Pattern)
  • 路由器概述
  • 学习最新vue20.17.0-事件处理
  • 从0开始深度学习(11)——多层感知机
  • 学习文档10/18
  • 关于k8s集群高可用性的探究
  • arm_acle.h找不到
  • web安全:应急响应
  • 滚雪球学Redis[5.2讲]:Redis持久化优化深度解析:RDB与AOF的策略选择与实践
  • Redis的6.0以上为啥又支持多线程
  • CentOS 7上安装MySQL客户端并进行配置
  • Vite创建Vue3项目以及Vue3相关基础知识
  • 【JavaEE】——TCP应答报文机制,超时重传机制
  • java散列表