Springboot接口参数校验

news2024/9/24 18:14:55

在设计接口时我们通常需要对接口中的非法参数做校验,以降低在程序运行时因为一些非法参数而导致程序发生异常的风险,例如登录的时候需要校验用户名密码是否为空,创建用户的时候需要校验邮件、手机号码格式是否准确。如果在代码中对接口参数一个个硬编码校验的话就太繁琐了,代码可读性极差。这时不妨试试Validator框架。

原生的依赖是validation-api而hibernate-validator是对validation-api的增强,hibernate-validator框架已经集成在spring-boot-starter-web中。

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.2.0.Final</version>
</dependency>

但从springboot-2.3开始,校验包被独立成了一个starter组件,所以需要引入validation和web,而springboot-2.3之前的版本只需要引入web依赖就可以了。 

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

部分常见注解如下:

注解功能
@AssertFalse可以为null,如果不为null的话必须为false
@AssertTrue可以为null,如果不为null的话必须为true
@DecimalMax设置不能超过最大值
@DecimalMin设置不能超过最小值
@Digits设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future日期必须在当前日期的未来
@Past日期必须在当前日期的过去
@Max最大不得超过此最大值
@Min最大不得小于此最小值
@NotNull不能为null,可以是空
@Null必须为null
@Pattern必须满足指定的正则表达式
@Size集合、数组、map等的size()值必须在指定范围内
@Email必须是email格式
@Length长度必须在指定范围内
@NotBlank字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range值必须在指定范围内
@URL必须是一个URL

一、基础注解使用 

注解简单演示类

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

@Data
public class UserVo {
    @Length(min = 4, max = 12, message = "账号长度必须位于4到10之间")
    private String userCode;
    @NotBlank(message = "用户名不能为空")
    private String userName;
    @Email(message = "邮箱格式有误")
    private String email;
    @Pattern(regexp = "^1[3456789]\\d{9}$", message = "手机号格式错误")
    private String phone;
    /**
     * 0:普通用户;VIP:会员用户;2:超级VIP用户
     */
    @Pattern(regexp = "[012]", message = "用户类型有误,请输入0:普通用户;VIP:会员用户;2:超级VIP用户")
    private String type;
}

在全局异常处理里面加上对参数校验异常的拦截

    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public ResponseVo validatedExceptionHandler(Exception e) {
        log.error("参数校验异常!原因是{}", e);
        if (e instanceof MethodArgumentNotValidException) {
            //BeanValidation exception
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
            return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),
                    ex.getBindingResult().getAllErrors().stream()
                            .map(ObjectError::getDefaultMessage)
                            .collect(Collectors.joining("; ")));
        } else if (e instanceof ConstraintViolationException) {
            //BeanValidation GET simple param
            ConstraintViolationException ex = (ConstraintViolationException) e;
            return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),
                    ex.getConstraintViolations().stream()
                            .map(ConstraintViolation::getMessage)
                            .collect(Collectors.joining("; ")));
        } else if (e instanceof BindException) {
            //BeanValidation GET object param
            BindException ex = (BindException) e;
            return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),
                    ex.getAllErrors().stream()
                            .map(ObjectError::getDefaultMessage)
                            .collect(Collectors.joining("; ")));
        }
        return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ResponseEnum.BODY_NOT_MATCH.getResultMsg());
    }

 定义参数校验的controller

import com.yx.light.element.mybatis.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.Email;

@RestController
@RequestMapping(value = "/valid")
@Validated
@Slf4j
public class ValidController {

    /**
     * 单参数校验,单参数需要在controller上加上@Validated配合使用
     * @param email
     * @return
     */
    @PostMapping(value = "/oneParam", produces = "application/json; charset=UTF-8")
    public String oneParam(@Email(message = "邮箱格式有误") String email) {
        log.info("校验邮箱为:{}", email);
        return "邮箱校验成功";
    }

    /**
     * body类型校验
     * @param vo
     * @return
     */
    @PostMapping(value = "/bodyParam", produces = "application/json; charset=UTF-8")
    public String bodyParam(@Validated @RequestBody UserVo vo) {
        log.info("校验body为:{}", vo);
        return "body校验成功";
    }

    /**
     * 表单参数校验
     * @param vo
     * @return
     */
    @PostMapping(value = "/formParam", produces = "application/json; charset=UTF-8")
    public String formParam(@Validated UserVo vo) {
        log.info("校验表单为:{}", vo);
        return "表单校验成功";
    }
}

oneParam接口

bodyParam接口

formParam接口

 二、自定义参数校验

当然你也可以自定义参数校验,出现有些复杂的逻辑基本注解是没办法兼容的,需要我们自己去实现自动校验。

自定义校验注解

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

@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = UserValidation.UniqueUserValidator.class)
public @interface UniqueUser {
    String message() default "用户编码、手机号码、邮箱不允许与现存用户重复";

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

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

 想让自定义验证注解生效,需要实现ConstraintValidator接口。接口的第一个参数是 自定义注解类型,第二个参数是 被注解字段的类。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.annotation.Annotation;
import java.util.function.Predicate;

@Slf4j
public class UserValidation<T extends Annotation> implements ConstraintValidator<T, UserVo> {

    protected Predicate<UserVo> predicate = c -> true;

    @Override
    public boolean isValid(UserVo user, ConstraintValidatorContext constraintValidatorContext) {
        return predicate.test(user);
    }

    /**
     * 校验用户是否唯一
     * 即判断数据库是否存在当前新用户的信息,如用户编码,手机,邮箱
     */
    public static class UniqueUserValidator extends UserValidation<UniqueUser> {
        @Override
        public void initialize(UniqueUser uniqueUser) {
            predicate = c -> !existsUser(c.getUserCode(), c.getEmail(), c.getPhone());
        }

        private boolean existsUser(String userCode, String email, String phone) {
            //TODO 写一个数据库查询判断是否存在相同的用户,省略...
            return true;
        }
    }
}

在controller中添加测试接口

@PostMapping(value = "/addUser", produces = "application/json; charset=UTF-8")
public String addUser(@UniqueUser @Validated @RequestBody UserVo vo) {
	log.info("校验body为:{}", vo);
	return "新增成功";
}

addUser接口 

 三、分组校验

如果你的一个实体中的字段某一些是新增的时候必传,某一些修改时又不用传,那么对于不用传的字段肯定不需要校验的,这时候如果我们共用一个实体作为多个接口参数那肯定存在兼容问题,此时你就可以考虑将参数分组判断。

定义一个分组接口ValidGroup让其继承javax.validation.groups.Default,再在分组接口中定义出多个不同的操作类型,Create,Update,Query,Delete。

import javax.validation.groups.Default;

public interface ValidGroup extends Default {

    interface Crud extends ValidGroup {
        interface Create extends Crud {

        }

        interface Update extends Crud {

        }

        interface Query extends Crud {

        }

        interface Delete extends Crud {

        }
    }
}

稍微修改一下原来的vo,给他们加上分组参数groups

import com.yx.light.element.mybatis.validation.ValidGroup;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

@Data
public class UserVo {
    @Length(min = 4, max = 12, message = "账号长度必须位于4到10之间", groups = ValidGroup.Crud.Update.class)
    private String userCode;
    @NotBlank(message = "用户名不能为空", groups = ValidGroup.Crud.Create.class)
    private String userName;
    @Email(message = "邮箱格式有误")
    private String email;
    @Pattern(regexp = "^1[3456789]\\d{9}$", message = "手机号格式错误")
    private String phone;
    /**
     * 0:普通用户;VIP:会员用户;2:超级VIP用户
     */
    @Pattern(regexp = "[012]", message = "用户类型有误,请输入0:普通用户;VIP:会员用户;2:超级VIP用户")
    private String type;
}

在controller中添加如下方法,这里我们通过value属性给addUserV2()和updateUser()方法分别指定Create和Update分组。

@PostMapping(value = "/addUserV2", produces = "application/json; charset=UTF-8")
public String addUserV2(@Validated(value = ValidGroup.Crud.Create.class) @RequestBody UserVo vo) {
	log.info("校验body为:{}", vo);
	return "新增成功";
}

@PostMapping(value = "/updateUser", produces = "application/json; charset=UTF-8")
public String updateUser(@Validated(value = ValidGroup.Crud.Update.class) @RequestBody UserVo vo) {
	log.info("校验body为:{}", vo);
	return "更新成功";
}

addUserV2接口

updateUser接口

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1483228.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React之组件定义和事件处理

一、组件的分类 在react中&#xff0c;组件分为函数组件和class组件&#xff0c;也就是无状态组件和有状态组件。 * 更过时候我们应该区别使用无状态组件&#xff0c;因为如果有状态组件会触发生命周期所对应的一些函数 * 一旦触发他生命周期的函数&#xff0c;它就会影响当前项…

Linux学习笔记:进程的终止和等待

进程终止和等待 进程终止进程退出场景进程常见退出方式_exit()退出exit()退出return退出exit()与_exit()的不同之处 进程的等待什么是进程等待?为什么要进行进程等待如何进行等待wait方式:waitpid方式 进程终止 进程退出场景 一般来讲,进程的退出场景有三种: 代码运行完毕,…

chalk库的使用

这篇文章主要是对chalk库官方文档的中文翻译以及我自己的一些理解。chalk的官方文档可以看这里。 首先说下chalk库的作用&#xff1a;美化终端输出的文本&#xff0c;例如添加不同的字体颜色、不同颜色的背景、粗体以及添加下划线等等&#xff0c;看下图&#xff1a; 优点 富…

Vue2->3

Vue2->3 认识Vue31. Vue2 选项式 API vs Vue3 组合式API2. Vue3的优势 使用create-vue搭建Vue3项目1. 认识create-vue2. 使用create-vue创建项目 熟悉项目和关键文件组合式API - setup选项1. setup选项的写法和执行时机2. setup中写代码的特点3. <script setup>语法糖…

基于springboot+vue的社区养老服务平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Kali Linux 安装 + 获取 root 权限 + 远程访问

一、什么是Kali kali是linux其中一个发行版&#xff0c;基于Debian&#xff0c;前身是BackTrack&#xff08;简称BT系统&#xff09;。kali系统内置大量渗透测试软件&#xff0c;可以说是巨大的渗透系统&#xff0c;涵盖了多个领域&#xff0c;如无线网络、数字取证、服务器、密…

【MySQL】表的内连和外连(重点)

表的连接分为内连和外连。 一、内连接 内连接实际上就是利用 where 子句对两种表形成的笛卡儿积进行筛选&#xff0c;前面学习的查询都是内连接&#xff0c;也是在开发过程中使用的最多的连接查询。 select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件; 注意&…

Java核心技术知识导航(周更)

写在文章开头 面试旺季笔者也收到很多读者的私信&#xff0c;所以笔者就对近期不断更新迭代补充的Java核心知识点进行一个导航汇总。感兴趣的读者建议收藏&#xff0c;这些专栏系列笔者会在每个周末进行迭代、补充、更新&#xff0c;希望对你有帮助。 你好&#xff0c;我叫sh…

算法学习路径图

1.总览 算法学习路径 2.学习资料 2.1 Java算法与数据结构 Java算法与数据结构

【C++提高编程】

C提高编程 C提高编程1 模板1.1 模板的概念1.2 函数模板1.2.1 函数模板语法1.2.2 函数模板注意事项1.2.3 函数模板案例1.2.4 普通函数与函数模板的区别1.2.5 普通函数与函数模板的调用规则1.2.6 模板的局限性 1.3 类模板1.3.1 类模板语法1.3.2 类模板与函数模板区别1.3.3 类模板…

运筹学_1.1.2 线性规划问题-图解法

1.1.2 线性规划问题-图解法 一、图解法求解步骤&#xff08;只适用于两个决策变量问题&#xff09;二、图解法作图实例三、图解法分析线性规划几种解的情况1、唯一最优解2、无穷多最优解3、无界解4、无解或无可行解 四、图解法的几点启示 一、图解法求解步骤&#xff08;只适用…

内网信息搜集

目录 内网基础知识 基本流程图 怎么判断是否在域内 常规信息类收集-应用&服务&权限等 cs信息搜集 bloodhound安装及使用 内网基础知识 工作组&#xff1a;将不同的计算机按照功能分别列入不同的组&#xff0c;想要访问某个部门的资源&#xff0c;只要在【网络】里…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的钢材表面缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发钢材表面缺陷检测系统对于保障制造质量和提高生产效率具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个钢材表面缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#…

【服务器数据恢复】昆腾存储中raid5磁盘阵列数据恢复案例

服务器数据恢复环境&故障&#xff1a; 10个磁盘柜&#xff0c;每个磁盘柜配24块硬盘。9个磁盘柜用于存储数据&#xff0c;1个磁盘柜用于存储元数据。 元数据存储中24块硬盘&#xff0c;组建了9组RAID1阵列1组RAID10阵列&#xff0c;4个全局热备硬盘。 数据存储中&#xff0…

Vue.js的双向绑定原理

Vue的双向绑定 vue双向绑定是其最重要的核心亮点&#xff0c;其原理也很简单&#xff0c;这里做个简单总结 vue2的双向绑定是利用的Object.definePropertyvue3的双向绑定是利用的 ES6Porxy中的defineProperty(target, propKey, propDesc 其作用类似于Object.defineProperty …

水库安全监测方案(福建地区水库安全监测案例分享)

我司星创易联最近在福建省受到了一个水库安全监测系统项目的委托。该水库位于福建中部山区,作为该地区的重要防洪与供水工程,对下游数十万人的生活产生重大影响。但是因为水库附近地质情况复杂,水库大坝在多次洪水冲击下出现一定病害,亟须全面加强对水库大坝安全状况的监测,以确…

C语言---文件操作(1)

1.文件的打开和关闭 fopen有2个参数&#xff0c;第一个是文件的名字&#xff0c;第二个是打开的模式&#xff08;例如是读文件还是写文件&#xff09; (1)该文件夹下面没有data.txt文件&#xff0c;但是我执行了read操作&#xff0c;所以会显示这样的错误 &#xff08;2&#…

php httpfs链接hdfs

一.代码&#xff08;有bug&#xff09; GitHub - michaelbutler/php-WebHDFS: A PHP client for WebHDFS 二.调用代码 1.代码1.代码 require_once(../webhdfs/src/org/apache/hadoop/WebHDFS.php);require_once(../webhdfs/src/org/apache/hadoop/tools/Curl.php); require_o…

YOLOv5 + Flask + Vue实现基于深度学习算法的垃圾检测系统源码+数据库

✨界面展示 登录 注册 垃圾检测 用户管理 404 Not Found页面 403 拒绝访问页面 黑暗模式 深蓝模式 灰色模式 色弱模式 ✨技术特性 深度学习 YOLOv5&#x1f680;&#xff1a;高效、准确的目标检测算法&#xff0c;实时识别检测图像和视频中的各种对象PyTorch&#xff1a;机器…

对于《幻兽帕鲁》这样的游戏,如何优化服务器性能以提高游戏体验?

对于《幻兽帕鲁》这样的游戏&#xff0c;如何评估和优化服务器性能以提高游戏体验&#xff1f; 硬件配置优化&#xff1a;选择高性能的服务器&#xff0c;如4核16G的幻兽帕鲁服务器&#xff0c;这样可以保证有足够的计算性能和内存容量来支持游戏的运行。同时&#xff0c;考虑到…