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 包括如下约束注解:

约束注解 说明
@Email 被注释的元素必须是电子邮箱地址
@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里参数校验/参数验证相关推荐

  1. 粉丝说SpringBoot集成validation校验参数有坑,我试了试

    公众号中分享了一篇文章,关于SpringBoot集成validation校验参数的,粉丝留言说有坑. 原留言如下: 有坑,你试试^A-\\d{12}-\\d{4}$,这条正则经过validate这个方 ...

  2. Springboot优雅的参数校验(一)

    目录 前言 1. 依赖引入 2. 参数形式 3. 常用到的约束注解 4. 参数基础校验 4.1 @RequestBody参数 4.2 @RequestParam参数/@PathVariable参数 4 ...

  3. bean validation校验方法参数_SpringBoot参数校验 从入门到精通 解决繁琐的参数验证工作...

    ● 手把手教你实现 SpringBoot与Vue整合开发 前后端分离 简单例子 详解●SQL优化经历  SQL执行效率提高了1000w倍●Java面试题 详解 由易到难● SQL语句大全详解 增删改查 ...

  4. springboot 参数校验详解

    https://www.jianshu.com/p/89a675b7c900 在日常开发写rest接口时,接口参数校验这一部分是必须的,但是如果全部用代码去做,显得十分麻烦,spring也提供了这部分 ...

  5. SpringBoot @Validated注解实现参数校验

    1. 前言 做web开发有一点很烦人就是要校验参数,基本上每个接口都要对参数进行校验,比如一些格式校验 非空校验都是必不可少的.如果参数比较少的话还是容易 处理的一但参数比较多了的话代码中就会出现大量 ...

  6. SpringBoot实现通用的接口参数校验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:cipher juejin.im/post/5af3c25b ...

  7. java 接口参数验证_SpringBoot实现通用的接口参数校验

    作者:cipher 来源:http://39sd.cn/560BA 本文介绍基于Spring Boot和JDK8编写一个AOP,结合自定义注解实现通用的接口参数校验. 缘由 目前参数校验常用的方法是在 ...

  8. boot spring 对参数检测_【springboot】@Valid参数校验

    转自: https://blog.csdn.net/cp026la/article/details/86495659 扯淡: 刚开始写代码的时候对参数的校验要么不做.要么写很多类似 if( xx == ...

  9. SpringBoot - 优雅的实现【自定义参数校验】高级进阶

    文章目录 Pre 概述 三部曲 Step1 搞自定义注解 Step2 搞校验逻辑 Step3 使用 Step4 验证 附 int 类型的判断 源码 Pre SpringBoot - 优雅的实现[参数校 ...

最新文章

  1. java 类中构造函数的讲解
  2. FPGA实现序列检测(训练testbench写法)
  3. (转)java动态代理与aop
  4. xdf文件改word_真正Txt 文本文件和Doc Word文件批量互转工具
  5. 联想笔记本插入耳机仍外放--解决方式
  6. 通用计算机不能直接硬件乘法,2018年4月自考《计算机组成原理》真题
  7. 机器学习(2): K-means (k均值) 聚类算法 小结
  8. 22年字节跳动飞书人力套件二面面经
  9. 【Flink实战系列】Flink 提交任务遇到 Server Response Internal server error 怎么排查
  10. Java spring boot 实现支付宝支付
  11. python 配置 mitmproxy 证书
  12. 平安科技实习生面试经历
  13. 将excel的单元格日期格式转换成文本格式
  14. 听见丨小鹏汽车上​市量产车G3发布 英特尔与法拉利合作 将人工智能技术用于赛车运动
  15. sync包——互斥锁
  16. 世相科技:引领新媒体时代的潮水方向
  17. Python OpenCV 横向平铺图像制作长图
  18. linux cpu 使用10个进程,linux下获取占用CPU资源最多的10个进程
  19. 利用树莓派(3B+)板载蓝牙(ble)实现与蓝牙热敏打印机的通信
  20. [ACFLY全新开源飞控--先知系列] ACFly Prophet

热门文章

  1. KVell: the Design and Implementation of a Fast Persistent Key-Value Store
  2. 多米诺骨牌上演:三箭资本崩盘始末
  3. ThinkPHP 微信支付及退款
  4. 百度TTS,支持离线环境下使用
  5. 缺陷定位之路在何方?论文阅读:Revisiting the practical use of automated software fault localization techniques
  6. 【历史上的今天】12 月 27 日:第一台计算机背后的女性们;Box 创始人出生;开普勒诞生
  7. cmd查看python库命令_怎么用命令查看python的库
  8. innobackupex全量恢复
  9. js拖拽图片到浏览器上传
  10. java二重积分_《University Calculus》-chaper13-多重积分-二重积分的计算