前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot参数校验的各种正确使用方式

SpringBoot参数校验的各种正确使用方式

作者头像
时间静止不是简史
发布2023-12-05 15:40:20
4960
发布2023-12-05 15:40:20
举报
文章被收录于专栏:Java探索之路

背景

最近因为某些原因, 需要对原来项目中通过注解实现的参数校验进行升级. 改为使用分组进行校验, 区分增删改查. 去网上看了一下, 结果发现相关文章大都是简单提一下, 实际使用中出现的参数失效的问题反而很多. 而且本来还打算将本文名称改成SpringBoot参数校验各种失效情景及解决, 但思考后发现失效的问题较多, 无法一一列举. 所以决定还是将SpringBoot参数校验的各种正确使用方式系统的总结一下, 以供后续自己和他人使用.

介绍

SpringBoot参数校验网上已经有很多了, 我这里不详细说明了. 就简单介绍下两注意三步骤

注意:

  • springboot 2.3之前的集成在spring-boot-starter-web里了,所以不需要额外引入包
  • springboot 2.3之后需要引入 spring-boot-starter-validation,否则校验不生效.

大致的使用步骤有三个步骤:

pom文件中加入相关启动器

代码语言:javascript
复制
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

请求实体对应属性上面加注解 & controller上加相关注解(主要是@Validated)

代码语言:javascript
复制
public class Item {
 
    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;
 
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private String props;
}

全局异常处理

代码语言:javascript
复制
//请求入参为实体类, 嵌套类时调用
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseTemplate MethodArgumentNotValidException(MethodArgumentNotValidException e) {
    List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
    List<String> resultMessageList = fieldErrors.stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage)
            .collect(Collectors.toList());
    return ResponseTemplate.fail(resultMessageList.toString().substring(1, resultMessageList.toString().length()-1));
}

//请求入参为list集合, 嵌套类时调用
@ExceptionHandler(ConstraintViolationException.class)
public ResponseTemplate handlerValidator(ConstraintViolationException constraintViolationException) {
    Set<ConstraintViolation<?>> constraintViolationSets = constraintViolationException.getConstraintViolations();
    List<String> resultMessageList = constraintViolationSets.stream()
            .map(ConstraintViolation::getMessageTemplate)
            .collect(Collectors.toList());
    return ResponseTemplate.fail(resultMessageList.toString().substring(1, resultMessageList.toString().length()-1));
}

常用参数校验方式

本文主要介绍post请求时, 入参为下图参数类型时的参数校验方式. 而get请求, 则只需保证在 controller上加@Validated并在入参前加入对应的用于校验的注解即可.

在这里插入图片描述
在这里插入图片描述

提前准备

在控制类上加@Validated

代码语言:javascript
复制
import com.sx.projectstructure.config.jsr.InsertDO;
import com.sx.projectstructure.config.jsr.UpdateDO;
import com.sx.projectstructure.entity.po.StudentInfoPO;
import com.sx.projectstructure.entity.vo.ResponseTemplate;
import com.sx.projectstructure.service.StudentInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;

/**
 * @Author chy
 * @Date 2023/11/28
 * @Description
 */
@RestController
@RequestMapping("/student/info")
@Api(tags = "学生信息接口")
@Validated
public class StudentInfoController {

    @Resource
    private StudentInfoService studentInfoService;

}

创建四个用于分组校验时使用的接口类

代码语言:javascript
复制
/**
 * @Author chy
 * @Date 2023/11/28
 * @Description
 *
 *      JSR330 删除组标识
 */
public interface DeleteDO {
}

/**
 * @Author chy
 * @Date 2023/11/28
 * @Description
 *
 *      JSR330 删除组标识
 */
public interface DeleteDO {
}

/**
 * @Author chy
 * @Date 2023/11/28
 * @Description
 *
 *     JSR330 查询组标识
 */
public interface SelectDO {
}

/**
 * @Author chy
 * @Date 2023/11/28
 * @Description
 *
 *      JSR 330 修改组标识
 */
public interface UpdateDO {
}

需要简单说明一下使用分组校验的原理: 就是在校验参数时, 我们希望有些参数仅在指定的操作中生效(例如增改). 我们就可以通过对应属性校验注解的groups参数指定参数校验生效的范围, 值为上面的接口(可以为多个). 然后在controller层通过@Validated(value = InsertDO.class) 来定义当前属于哪种操作并和请求实体中定义的范围进行比对. 属于对应范围后才会进行参数校验. 具体请求方式的校验步骤请见下面的介绍:

普通实体

分组

请求实体 groups 可以定义校验生效的范围, 表示在操作下用于参数校验的注解才会生效.

代码语言:javascript
复制
@TableName(value = "student_info")
public class StudentInfoPO {
    
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @NotEmpty(message = "菜单名称不可为空", groups = {InsertDO.class, UpdateDO.class})
    @HaveNoBlank(message = "字符串中不能含有空格666", groups = {InsertDO.class})
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @NotNull(message = "年龄不可为空", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "age")
    private Integer age;

    /**
     * 身份证号
     */
    @NotEmpty(message = "身份证号不可为空", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "id_card")
    private String idCard;
}    

接口方法 @Validatedvalue属性可以指定当前属于哪种操作, 用于和请求实体中生效的范围进行比较, 属于生效范围中才会进行参数校验

代码语言:javascript
复制
    @PostMapping
    @ApiOperation("测试新增-post请求")
    public ResponseTemplate insertStudentInfo(@RequestBody @Validated(value = InsertDO.class) StudentInfoPO studentInfoPO) {
        studentInfoService.insertStudentInfo(studentInfoPO);
        return ResponseTemplate.success();
    }

无参请求 因为使用的基本上都是判空校验的注解, 所以使用无参和实参进行请求, 通过返回信息来判断是否生效

在这里插入图片描述
在这里插入图片描述

实参请求

在这里插入图片描述
在这里插入图片描述

ps: 验证controller方法中@Validated的值为请求实体中groups 属性范围外的值时, 是否生效

修改controller方法中@Validated注解中value属性的值

代码语言:javascript
复制
    @PostMapping
    @ApiOperation("测试新增-post请求-分组")
    public ResponseTemplate insertStudentInfo(@RequestBody @Validated(value = SelectDO.class) StudentInfoPO studentInfoPO) {
        studentInfoService.insertStudentInfo(studentInfoPO);
        return ResponseTemplate.success();
    }

通过postman可以看到, 在指定范围外的分组进行校验时, 无参请求, 接口也正常调用, 所以参数校验注解没有生效

在这里插入图片描述
在这里插入图片描述
不分组

请求实体 同一个参数校验注解分组和不分组时不能同时混用, 不分组时需要去掉groups参数相关内容. controller方法中请求实体前使用未分组的注解, 但请求实体具体属性上注解使用分组属性时, 则参数校验不生效 因此建议使用同一个请求实体时不要同时出现这两种方式, 否则可能会出现滥用导致的注解失效的问题.

代码语言:javascript
复制
@TableName(value = "student_info")
public class StudentInfoPO {

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @NotEmpty(message = "菜单名称不可为空")
    @HaveNoBlank(message = "字符串中不能含有空格666")
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @NotNull(message = "年龄不可为空")
    @TableField(value = "age")
    private Integer age;

    /**
     * 身份证号
     */
    @NotEmpty(message = "身份证号不可为空")
    @TableField(value = "id_card")
    private String idCard;
}    

接口方法 需要注意: 虽然controller类上面已经有@Validated, 但请求实体旁还是需要有, 否则不会生效

代码语言:javascript
复制
    @PostMapping("/noGroup")
    @ApiOperation("测试新增-post请求-不分组")
    public ResponseTemplate insertStudentInfo2(@RequestBody @Validated StudentInfoPO studentInfoPO) {
        studentInfoService.insertStudentInfo(studentInfoPO);
        return ResponseTemplate.success();
    }

无参请求

在这里插入图片描述
在这里插入图片描述

实参请求

在这里插入图片描述
在这里插入图片描述

List集合

分组

本来由普通实体,分组 + List集合,不分组可以推得该部分, 但是由于两个注解不会同时生效(如下代码). 因此想要实现此效果需要自己自定义注解 (后续有时间的话会出专门的文章给出具体实现步骤)

代码语言:javascript
复制
	//@Valid和@Validated不会同时生效
    @PostMapping("/saveGroup")
    @ApiOperation("测试新增-post请求-list集合对象-分组")
    public ResponseTemplate insertStudentInfo3(@RequestBody @Valid @Validated(value = InsertDO.class) List<StudentInfoPO> studentInfoPO) {
        System.out.println("studentInfoPO = " + studentInfoPO);
        return ResponseTemplate.success();
    }
不分组

请求实体

代码语言:javascript
复制
@TableName(value = "student_info")
public class StudentInfoPO {

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @NotEmpty(message = "菜单名称不可为空")
    @HaveNoBlank(message = "字符串中不能含有空格666", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @NotNull(message = "年龄不可为空")
    @TableField(value = "age")
    private Integer age;

    /**
     * 身份证号
     */
    @NotEmpty(message = "身份证号不可为空")
    @TableField(value = "id_card")
    private String idCard;
}    

controller方法

代码语言:javascript
复制
    @PostMapping("/save")
    @ApiOperation("测试新增-post请求-list集合对象请求")
    public ResponseTemplate insertStudentInfo2(@RequestBody @Valid List<StudentInfoPO> studentInfoPO) {
        System.out.println("studentInfoPO = " + studentInfoPO);
        return ResponseTemplate.success();
    }

无参请求

在这里插入图片描述
在这里插入图片描述

带参请求

在这里插入图片描述
在这里插入图片描述

嵌套

分组

请求实体 这里一定要对嵌套实体的属性加上@Valid, 用于对被嵌套的实体类进行校验 可以加@NotNull, 用于嵌套实体为null时的提示, 否则不会提示

代码语言:javascript
复制
@TableName(value = "student_info")
public class StudentInfoPO {

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
//    @NotEmpty(message = "菜单名称不可为空")
//    @HaveNoBlank(message = "字符串中不能含有空格666", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
//    @NotNull(message = "年龄不可为空", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "age")
    private Integer age;

    /**
     * 身份证号
     */
//    @NotEmpty(message = "身份证号不可为空", groups = {InsertDO.class, UpdateDO.class})
    @TableField(value = "id_card")
    private String idCard;

    @TableField(value = "create_time")
    private LocalDateTime createTime;

    @TableField(value = "update_time")
    private LocalDateTime updateTime;

    @Valid
    @NotNull(message = "demoDto不能为空")
    private DemoDto demoDto;
}

嵌套实体 通过groups指定生效的范围

代码语言:javascript
复制
public class DemoDto {

    @NotNull(message = "嵌套类param属性不能为null",groups = {InsertDO.class, UpdateDO.class})
    @Min(value = 2,groups = {InsertDO.class, UpdateDO.class})
    private Integer param;

    public Integer getParam() {
        return param;
    }

    public void setParam(Integer param) {
        this.param = param;
    }
}

controller方法 同普通实体分组一样, 分组. @Validatedvalue属性可以对请求实体或者嵌套实体中规定的范围进行比对

代码语言:javascript
复制
    @PostMapping
    @ApiOperation("测试新增-post请求-分组")
    public ResponseTemplate insertStudentInfo(@RequestBody @Validated(value = InsertDO.class) StudentInfoPO studentInfoPO) {
//        studentInfoService.insertStudentInfo(studentInfoPO);
        return ResponseTemplate.success();
    }

传入嵌套实体, 传入指定参数, 但传入不合规的值

在这里插入图片描述
在这里插入图片描述

传入嵌套实体, 传入指定参数, 传入合规的值

在这里插入图片描述
在这里插入图片描述
不分组

请求实体 这里一定要对嵌套实体对应属性加上@Valid, 用于对被嵌套的实体类进行校验 建议可以追加@NotNull注解, 用于嵌套属性为null时的消息提示, 否则嵌套属性不传时, 则不会对嵌套属性和其对应的嵌套实体内的属性进行校验.

代码语言:javascript
复制
@TableName(value = "student_info")
public class StudentInfoPO {

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @TableField(value = "name")
    private String name;

    /**
     * 年龄
     */
    @TableField(value = "age")
    private Integer age;

    /**
     * 身份证号
     */
    @TableField(value = "id_card")
    private String idCard;

	/**
     * 嵌套属性
     */
    @Valid
    @NotNull(message = "demoDto不能为空")
    private DemoDto demoDto;
 }   

嵌套实体 可以加入需要进行校验的属性

代码语言:javascript
复制
public class DemoDto {

    @NotNull(message = "嵌套类param属性不能为null")
    @Min(2)
    private Integer param;

    public Integer getParam() {
        return param;
    }

    public void setParam(Integer param) {
        this.param = param;
    }
}

controller方法 这里同普通实体, 不分组的书写方式

代码语言:javascript
复制
    @PostMapping("/noGroup")
    @ApiOperation("测试新增-post请求-不分组")
    public ResponseTemplate insertStudentInfo2(@RequestBody @Validated StudentInfoPO studentInfoPO) {
        studentInfoService.insertStudentInfo(studentInfoPO);
        return ResponseTemplate.success();
    }

没有传入嵌套实体时请求接口

在这里插入图片描述
在这里插入图片描述

传入嵌套实体, 但不传指定参数时

在这里插入图片描述
在这里插入图片描述

传入嵌套实体, 传入指定参数

在这里插入图片描述
在这里插入图片描述

传入嵌套实体, 传入按照要求的指定参数

在这里插入图片描述
在这里插入图片描述

注意事项

@valid 和@Validated 区别

在这里插入图片描述
在这里插入图片描述

参数校验常用注解介绍

在这里插入图片描述
在这里插入图片描述

关于get请求的参数校验

get请求很简单, 只需要在controller方法上加上@Validated, 在指定参数前加上校验注解即可

代码语言:javascript
复制
@RestController
@RequestMapping("/student/info")
@Validated
public class StudentInfoController {

 @GetMapping("checkParam")
 public String checkParam(@RequestParam  @Max(value = 99, message = "不能大于99岁") Integer age) {
  return "ok";
 }

  @GetMapping("checkPath/{id}")
 public String checkPath(@PathVariable  @Pattern(regexp = "^[0-9]*$", message = "id参数值必须是正整数") String id)   {
  return "ok";
 }
}

关于post请求的参数校验总结

post请求常用参数校验方式总结

在这里插入图片描述
在这里插入图片描述

参考: https://blog.csdn.net/Zong_0915/article/details/126649671 https://blog.csdn.net/sun_mu_one/article/details/122056101 https://zhuanlan.zhihu.com/p/646379747

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 介绍
  • 常用参数校验方式
    • 提前准备
      • 普通实体
        • 分组
        • 不分组
      • List集合
        • 分组
        • 不分组
      • 嵌套
        • 分组
        • 不分组
    • 注意事项
      • @valid 和@Validated 区别
        • 参数校验常用注解介绍
          • 关于get请求的参数校验
            • 关于post请求的参数校验总结
            相关产品与服务
            腾讯云服务器利旧
            云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档