SpringBoot里参数校验/参数验证
1、前言
在控制器类的方法里自己写校验逻辑代码当然也可以,只是代码比较丑陋,有点“low”。业界有更好的处理方法,分别阐述如下。
2、PathVariable校验
@GetMapping("/path/{group:[a-zA-Z0-9_]+}/{userid}")
@ResponseBody
public String path(@PathVariable("group") String group, @PathVariable("userid") Integer userid) {return group + ":" + userid;
}
用法是:路径变量:正则表达式。当请求URI不满足正则表达式时,客户端将收到404错误码。不方便的地方是,不能通过捕获异常的方式,向前端返回统一的、自定义格式的响应参数。
3、方法参数校验
@GetMapping("/validate1")
@ResponseBody
public String validate1(@Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age) {return "validate1";
}
如果前端传递的参数不满足规则,则抛出异常。注解Size、Min、Max来自validation-api.jar,更多注解参见相关标准小节。
4、表单对象/VO对象校验
当参数是VO时,可以在VO类的属性上添加校验注解。
public class User {@Size(min = 1,max = 10,message = "姓名长度必须为1到10")private String name;@NotEmptyprivate String firstName;@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100")private Integer age;@Future@JSONField(format="yyyy-MM-dd HH:mm:ss")private Date birth;。。。
}
其中,Future注解要求必须是相对当前时间来讲“未来的”某个时间。
@PostMapping("/validate2")
@ResponseBody
public User validate2(@Valid @RequestBody User user){return user;
}
5、自定义校验规则
5.1 自定义注解校验
需要自定义一个注解类和一个校验类。
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER,ElementType.FIELD})
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator {// flag的有效值,多个使用,隔开String values();// flag无效时的提示内容String message() default "flag必须是预定义的那几个值,不能随便写";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;public class FlagValidatorClass implements ConstraintValidator<FlagValidator,Object> {/*** FlagValidator注解规定的那些有效值*/private String values;@Overridepublic void initialize(FlagValidator flagValidator) {this.values = flagValidator.values();}/*** 用户输入的值,必须是FlagValidator注解规定的那些值其中之一。* 否则,校验不通过。* @param value 用户输入的值,如从前端传入的某个值*/@Overridepublic boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {// 切割获取值String[] value_array = values.split(",");Boolean isFlag = false;for (int i = 0; i < value_array.length; i++){// 存在一致就跳出循环if (value_array[i] .equals(value)){isFlag = true; break;}}return isFlag;}
}
使用我们自定义的注解:
public class User {// 前端传入的flag值必须是1或2或3,否则校验失败@FlagValidator(values = "1,2,3")private String flag ;。。。
}
5.2 分组校验
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;public class Resume {public interface Default {}public interface Update {}@NotNull(message = "id不能为空", groups = Update.class)private Long id;@NotNull(message = "名字不能为空", groups = Default.class)@Length(min = 4, max = 10, message = "name 长度必须在 {min} - {max} 之间", groups = Default.class)private String name;@NotNull(message = "年龄不能为空", groups = Default.class)@Min(value = 18, message = "年龄不能小于18岁", groups = Default.class)private Integer age;。。。
}
/*** 使用Defaul分组进行验证* @param resume* @return*/@PostMapping("/validate5")public String addUser(@Validated(value = Resume.Default.class) @RequestBody Resume resume) {return "validate5";}/*** 使用Default、Update分组进行验证* @param resume* @return*/@PutMapping("/validate6")public String updateUser(@Validated(value = {Resume.Update.class, Resume.Default.class}) @RequestBody Resume resume) {return "validate6";}
建立了两个分组,名称分别为Default、Update。POST方法提交时使用Defaut分组的校验规则,PUT方法提交时同时使用两个分组规则。
6、异常拦截器
通过设置全局异常处理器,统一向前端返回校验失败信息。
import com.scj.springbootdemo.WebResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;/*** 全局异常处理器*/
@ControllerAdvice
public class GlobalExceptionHandler {private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 用来处理bean validation异常* @param ex* @return*/@ExceptionHandler(ConstraintViolationException.class)@ResponseBodypublic WebResult resolveConstraintViolationException(ConstraintViolationException ex){WebResult errorWebResult = new WebResult(WebResult.FAILED);Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();if(!CollectionUtils.isEmpty(constraintViolations)){StringBuilder msgBuilder = new StringBuilder();for(ConstraintViolation constraintViolation :constraintViolations){msgBuilder.append(constraintViolation.getMessage()).append(",");}String errorMessage = msgBuilder.toString();if(errorMessage.length()>1){errorMessage = errorMessage.substring(0,errorMessage.length()-1);}errorWebResult.setInfo(errorMessage);return errorWebResult;}errorWebResult.setInfo(ex.getMessage());return errorWebResult;}@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseBodypublic WebResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){WebResult errorWebResult = new WebResult(WebResult.FAILED);List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();if(!CollectionUtils.isEmpty(objectErrors)) {StringBuilder msgBuilder = new StringBuilder();for (ObjectError objectError : objectErrors) {msgBuilder.append(objectError.getDefaultMessage()).append(",");}String errorMessage = msgBuilder.toString();if (errorMessage.length() > 1) {errorMessage = errorMessage.substring(0, errorMessage.length() - 1);}errorWebResult.setInfo(errorMessage);return errorWebResult;}errorWebResult.setInfo(ex.getMessage());return errorWebResult;}
}
7、相关标准
JSR 303 是Bean验证的规范 ,Hibernate Validator 是该规范的参考实现,它除了实现规范要求的注解外,还额外实现了一些注解。
validation-api-1.1.0.jar 包括如下约束注解:
约束注解 | 说明 |
---|---|
@AssertFalse | 被注释的元素必须为 false |
@AssertTrue | 被注释的元素必须为 true |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
hibernate-validator-5.3.6.jar 包括如下约束注解:
约束注解 | 说明 |
---|---|
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotBlank | 被注释的字符串的必须非空 |
@NotEmpty | 被注释的字符串、集合、Map、数组必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
@SafeHtml | 被注释的元素必须是安全Html |
@URL | 被注释的元素必须是有效URL |
略 |
8、参数校验原理
这篇文章 写得比较深入,我没有太理解。
9、本文源码
公司不让上传源码到GitHub,可以参加这篇文章。
10、同时校验2个或更多个字段/参数
常见的场景之一是,查询某信息时要输入开始时间和结束时间。显然,结束时间要≥开始时间。可以在查询VO类上使用自定义注解,下面的例子来自这里。划重点:@ValidAddress使用在类上。
@ValidAddress
public class Address {@NotNull@Size(max = 50)private String street1;@Size(max = 50)private String street2;@NotNull@Size(max = 10)private String zipCode;@NotNull@Size(max = 20)private String city;@Valid@NotNullprivate Country country;// Getters and setters
}
public class Country {@NotNull@Size(min = 2, max = 2)private String iso2;// Getters and setters
}
@Documented
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = { MultiCountryAddressValidator.class })
public @interface ValidAddress {String message() default "{com.example.validation.ValidAddress.message}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
public class MultiCountryAddressValidator implements ConstraintValidator<ValidAddress, Address> {public void initialize(ValidAddress constraintAnnotation) {}@Overridepublic boolean isValid(Address address, ConstraintValidatorContext constraintValidatorContext) {Country country = address.getCountry();if (country == null || country.getIso2() == null || address.getZipCode() == null) {return true;}switch (country.getIso2()) {case "FR":return // Check if address.getZipCode() is valid for Francecase "GR":return // Check if address.getZipCode() is valid for Greecedefault:return true;}}
}
SpringBoot里参数校验/参数验证相关推荐
- 粉丝说SpringBoot集成validation校验参数有坑,我试了试
公众号中分享了一篇文章,关于SpringBoot集成validation校验参数的,粉丝留言说有坑. 原留言如下: 有坑,你试试^A-\\d{12}-\\d{4}$,这条正则经过validate这个方 ...
- Springboot优雅的参数校验(一)
目录 前言 1. 依赖引入 2. 参数形式 3. 常用到的约束注解 4. 参数基础校验 4.1 @RequestBody参数 4.2 @RequestParam参数/@PathVariable参数 4 ...
- bean validation校验方法参数_SpringBoot参数校验 从入门到精通 解决繁琐的参数验证工作...
● 手把手教你实现 SpringBoot与Vue整合开发 前后端分离 简单例子 详解●SQL优化经历 SQL执行效率提高了1000w倍●Java面试题 详解 由易到难● SQL语句大全详解 增删改查 ...
- springboot 参数校验详解
https://www.jianshu.com/p/89a675b7c900 在日常开发写rest接口时,接口参数校验这一部分是必须的,但是如果全部用代码去做,显得十分麻烦,spring也提供了这部分 ...
- SpringBoot @Validated注解实现参数校验
1. 前言 做web开发有一点很烦人就是要校验参数,基本上每个接口都要对参数进行校验,比如一些格式校验 非空校验都是必不可少的.如果参数比较少的话还是容易 处理的一但参数比较多了的话代码中就会出现大量 ...
- SpringBoot实现通用的接口参数校验
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:cipher juejin.im/post/5af3c25b ...
- java 接口参数验证_SpringBoot实现通用的接口参数校验
作者:cipher 来源:http://39sd.cn/560BA 本文介绍基于Spring Boot和JDK8编写一个AOP,结合自定义注解实现通用的接口参数校验. 缘由 目前参数校验常用的方法是在 ...
- boot spring 对参数检测_【springboot】@Valid参数校验
转自: https://blog.csdn.net/cp026la/article/details/86495659 扯淡: 刚开始写代码的时候对参数的校验要么不做.要么写很多类似 if( xx == ...
- SpringBoot - 优雅的实现【自定义参数校验】高级进阶
文章目录 Pre 概述 三部曲 Step1 搞自定义注解 Step2 搞校验逻辑 Step3 使用 Step4 验证 附 int 类型的判断 源码 Pre SpringBoot - 优雅的实现[参数校 ...
最新文章
- java 类中构造函数的讲解
- FPGA实现序列检测(训练testbench写法)
- (转)java动态代理与aop
- xdf文件改word_真正Txt 文本文件和Doc Word文件批量互转工具
- 联想笔记本插入耳机仍外放--解决方式
- 通用计算机不能直接硬件乘法,2018年4月自考《计算机组成原理》真题
- 机器学习(2): K-means (k均值) 聚类算法 小结
- 22年字节跳动飞书人力套件二面面经
- 【Flink实战系列】Flink 提交任务遇到 Server Response Internal server error 怎么排查
- Java spring boot 实现支付宝支付
- python 配置 mitmproxy 证书
- 平安科技实习生面试经历
- 将excel的单元格日期格式转换成文本格式
- 听见丨小鹏汽车上​市量产车G3发布 英特尔与法拉利合作 将人工智能技术用于赛车运动
- sync包——互斥锁
- 世相科技:引领新媒体时代的潮水方向
- Python OpenCV 横向平铺图像制作长图
- linux cpu 使用10个进程,linux下获取占用CPU资源最多的10个进程
- 利用树莓派(3B+)板载蓝牙(ble)实现与蓝牙热敏打印机的通信
- [ACFLY全新开源飞控--先知系列] ACFly Prophet
热门文章
- KVell: the Design and Implementation of a Fast Persistent Key-Value Store
- 多米诺骨牌上演:三箭资本崩盘始末
- ThinkPHP 微信支付及退款
- 百度TTS,支持离线环境下使用
- 缺陷定位之路在何方?论文阅读:Revisiting the practical use of automated software fault localization techniques
- 【历史上的今天】12 月 27 日:第一台计算机背后的女性们;Box 创始人出生;开普勒诞生
- cmd查看python库命令_怎么用命令查看python的库
- innobackupex全量恢复
- js拖拽图片到浏览器上传
- java二重积分_《University Calculus》-chaper13-多重积分-二重积分的计算