接口参数校验(不使用hibernate-validator,规避大量if else)
引言
编写接口时,常用的参数校验使用hibernate-validator注解+@Validated注解进行参数校验。当遇到一些特殊场景或需求,需要自己对参数进行手动校验时,会出现以下问题:
不可避免的需要对接受的参数进行判断,此时便会出现大量if…else…影响代码可读性,且校验不够优雅。
本文给出一个参数校验方案,给大家一个思路。如果只是想使用,基本校验的已足够。
使用举例
具体项目中是如何使用的,可以参考博客中我正在开发的U-Learning后端开发日志,其中有GitHub项目地址
Before1
@PostMapping("/save")
public ResponseEntity<JsonResult> save(TeacherDto teacher) {
if(StringUtil.isEmpty(teacher.getTeaName())){
return ...;
}
if(StringUtil.isEmpty(teacher.getTeaNumber())){
return ...;
}
if(StringUtil.isEmpty(teacher.getTeaEmail())){
return ...;
}
//接口处理业务代码
...
}
After1
@PostMapping("/save")
public ResponseEntity<JsonResult> save(TeacherDto teacher) {
ValidatorBuilder.build()
.on(StringUtil.isEmpty(teacher.getTeaName()), SystemErrorCodeEnum.NAME_CANNOT_BE_NULL)
.on(StringUtil.isEmpty(teacher.getTeaNumber()), SystemErrorCodeEnum.TEA_NUMBER_CANNOT_BE_NULL)
.on(StringUtil.isEmpty(teacher.getTeaEmail()), SystemErrorCodeEnum.EMAIL_CANNOT_BE_NULL)
.doValidate().checkResult();
//接口处理业务代码
...
}
Beafore2
当你只有一个需要校验的参数时,可能会觉得这有些啰嗦
@GetMapping("/delete")
public ResponseEntity<JsonResult> delete(Long id) {
if(StringUtil.isEmpty(id)){
return ...;
}
//接口处理业务代码
...
}
After2
@GetMapping("/delete")
public ResponseEntity<JsonResult> delete(Long id) {
ValidateHandler.checkParameter(StringUtil.isEmpty(id), SystemErrorCodeEnum.ID_CANNOT_BE_NULL);
//接口处理业务代码
...
}
知识点
涉及到的知识点
- 建造者模式
- 链式调用
- 枚举
- 异常
- 继承与多态
拓展所需知识点
- 委托模式
使用前准备
自定义异常
public class BaseException extends RuntimeException {
private Integer status = HttpStatus.BAD_REQUEST.value();
public BaseException(Integer status) {
this.status = status;
}
public BaseException(BaseEnum baseEnum) {
super(baseEnum.getMessage());
this.status = baseEnum.getCode();
}
public BaseException(String message) {
super(message);
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(Throwable cause) {
super(cause);
}
public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public Integer getStatus() {
return status;
}
}
举个栗子
@Getter
public class BadRequestException extends BaseException {
public BadRequestException(String msg) {
super(msg);
}
public BadRequestException(BaseEnum baseEnum) {
super(baseEnum);
}
}
统一异常管理
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理系统自定义异常
*/
@ExceptionHandler(BaseException.class)
public ResponseEntity handleBaseException(BaseException e) {
String message = StringUtil.isContainChinese(e.getMessage()) ? e.getMessage() : null;
return ResponseEntityUtil.badRequest(JsonResult.buildErrorMsg(e.getStatus(),
Optional.ofNullable(message).orElse(MicroErrorCodeEnum.OPERATE_ERROR.getMessage())));
}
/**
* Throwable
* 接收非系统预测内的异常
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity handleThrowable(Throwable e) {
log.error("系统捕捉Throwable异常并处理 ==> " + e.getMessage(), e);
String message = StringUtil.isContainChinese(e.getMessage()) ? e.getMessage() : null;
return ResponseEntityUtil.internalServerError(JsonResult.buildErrorMsg(HttpStatus.INTERNAL_SERVER_ERROR.value(),
Optional.ofNullable(message).orElse(MicroErrorCodeEnum.SYSTEM_ERROR.getMessage())));
}
}
自定义枚举
基础枚举接口
public interface BaseEnum {
Integer getCode();
String getMessage();
}
举个栗子(根据自己需求创建)
public enum SystemErrorCodeEnum implements BaseEnum {
/**
* 后台管理系统错误状态码
*/
PARAMETER_EMPTY(HttpStatus.BAD_REQUEST, "参数不可为空!"),
TEACHER_NOT_EXISTS(HttpStatus.BAD_REQUEST, "教师不存在!"),
ID_CANNOT_BE_NULL(HttpStatus.BAD_REQUEST, "ID不可为空!"),
EMAIL_CANNOT_BE_NULL(HttpStatus.BAD_REQUEST, "邮箱不可为空!"),
;
private Integer code;
private String message;
SystemErrorCodeEnum(HttpStatus httpStatus, String message) {
this.code = httpStatus.value();
this.message = message;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
Validate核心代码
校验类context
@Data
@AllArgsConstructor
public class ValidatorContext {
/** 校验结果 */
private Boolean checkResult;
/** 错误信息枚举 */
private BaseEnum baseEnum;
}
校验类上下文
public class ValidatorHolder {
/** context集合 */
private List<ValidatorContext> validatorContexts;
/** 校验结果 */
private Boolean result;
/** 若result = true,保存错误信息 */
private BaseEnum baseEnum;
/** 初始化 */
public ValidatorHolder() {
this.validatorContexts = new ArrayList<>();
this.result = true;
this.baseEnum = null;
}
/** 校验链 */
public ValidatorHolder on(Boolean checkResult, BaseEnum baseEnum){
if(checkResult != null && baseEnum != null){
addContext(checkResult, baseEnum);
}
return this;
}
/** 添加待校验属性和错误信息 */
private void addContext(Boolean checkResult, BaseEnum baseEnum){
validatorContexts.add(new ValidatorContext(checkResult, baseEnum));
}
/** 校验 */
public ValidatorHolder doValidate(){
for (ValidatorContext validatorContext : validatorContexts) {
if(validatorContext.getCheckResult()){
result = false;
baseEnum = validatorContext.getBaseEnum();
break;
}
}
return this;
}
public void checkResult(){
ValidateHandler.checkValidator(this);
}
public Boolean getResult() {
return result;
}
public BaseEnum getBaseEnum() {
return baseEnum;
}
}
校验builder类
public class ValidatorBuilder {
public static ValidatorHolder build(){
return new ValidatorHolder();
}
}
校验处理器
参考Guava类库中提供的一个作参数检查的工具类--Preconditions类
public class ValidateHandler {
/**
* 判断校验是否成功,若存在错误,抛出异常
* 针对ValidatorHolder
*/
public static void checkValidator(ValidatorHolder validator){
if(! validator.getResult()){
throw new BadRequestException(validator.getBaseEnum());
}
}
/**
* 针对单参数校验
*
* @param checkResult true:参数错误; false:参数正确
* @param baseEnum 错误码
*/
public static void checkParameter(Boolean checkResult, BaseEnum baseEnum){
if(checkResult){
throw new BadRequestException(baseEnum);
}
}
}
接口参数校验(不使用hibernate-validator,规避大量if else)的更多相关文章
- Spring Boot 之:接口参数校验
Spring Boot 之:接口参数校验,学习资料 网址 SpringBoot(八) JSR-303 数据验证(写的比较好) https://qq343509740.gitee.io/2018/07/ ...
- Spring Boot实现通用的接口参数校验
Spring Boot实现通用的接口参数校验 Harries Blog™ 2018-05-10 2418 阅读 http ACE Spring App API https AOP apache IDE ...
- SpringBoot实现通用的接口参数校验
本文介绍基于Spring Boot和JDK8编写一个AOP,结合自定义注解实现通用的接口参数校验. 缘由 目前参数校验常用的方法是在实体类上添加注解,但对于不同的方法,所应用的校验规则也是不一样的,例 ...
- springboot 接口参数校验
前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...
- 接口参数校验之@Valid与BindingResult
接口方法往往需要对入参做一些校验,从而判断入参是否合格,而javax.validation包为我们提供了一些常用的参数校验注解,使用起来很方便. 下面这个示例是检验入参对象中的password是否为空 ...
- 【快学springboot】4.接口参数校验
前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...
- 使用hibernate validate做参数校验
1.为什么使用hibernate validate 在开发http接口的时候,参数校验是必须有的一个环节,当参数校验较少的时候,一般是直接按照校验条件做校验,校验不通过,返回错误信息.比如以下校验 ...
- Spring Boot 参数校验
1.背景介绍 开发过程中,后台的参数校验是必不可少的,所以经常会看到类似下面这样的代码 这样写并没有什么错,还挺工整的,只是看起来不是很优雅而已. 接下来,用Validation来改写这段 2.Spr ...
- spring注解式参数校验
很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者返回异常时的校验信息,在代码中相当冗长,今天我们就来学习spring注解式参数校验. 其实就是:hibernate的validator. 开始啦. ...
随机推荐
- 【Linux常见命令】seq命令
seq - print a sequence of numbers seq命令用于以指定增量从首数开始打印数字到尾数,即产生从某个数到另外一个数之间的所有整数,并且可以对整数的格式.宽度.分割符号进行 ...
- Vant Weapp小程序蹲坑之使用card组件显示价格
问题 在基于mpvue+Vant Weapp组件库实战过程中,问题越来越多.网络上所谓的"坑"总结,仅仅不过是其开发中所遭所遇之"坑"而已--估计后面的&quo ...
- Nginx重写请求后将url?后的参数去除
2019独角兽企业重金招聘Python工程师标准>>> 使用?结尾 注意,关键点就在于"?"这个尾缀.重定向的目标地址结尾处如果加了?号,则不会再转发传递 ...
- resetlogs 打开数据库时新生成日志位置问题
若系统中缺少联机日志,以resetlogs方式重建控制文件,那么当我们以alter database open resetlogs方式打开数据库时,新生成的联机日志会位于何处? 一:下面分别讨论几种情 ...
- P5522 [yLOI2019] 棠梨煎雪
updata on 2020.3.19 今天把博客从洛谷往博客园搬,图炸了 其实早就发现了,懒得管 那图其实就是一个用dev自带的调试功能调试时,RE了的报错 当时觉得很奇怪看不出是啥,现在再看已经觉 ...
- W - Doom HDU - 5239 线段树 找取模的规律+求一个很大的数的平方对一个数取模的写法 特别的模数==2^63-2^31
这个题目一开始感觉还是有点难的,这个模数这么大,根本就不知道怎么写,然后去搜了题解,知道了怎么去求当x很大的时候x的平方对一个数取模怎么样不会爆掉. 然后还顺便发现了一个规律就是当一个数更新一定次数之 ...
- SpringBoot:静态资源映射、定制404、配置icon
目录 静态资源映射规则 定制首页 定制错误页面 配置 icon 静态资源映射规则.定制首页.定制404页面.配置网站的图标 静态资源映射规则 SpringBoot中对于静态资源(css,js,img. ...
- mysql优化–explain分析sql语句执行效率
Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优 ...
- js函数传递参数的方式------传值与传递指针
原则: 1. 基本类型:传值 2. 对象:传递指针 应用场景之一: 用jq选择器获取某个div后(例如:element),准备进行某些修改,之后添加到页面中去. 采取例一的方式,append后发现修改 ...
- 心路历程-安装Docker
心路历程-安装Docker 本机环境 Windows10 激活HyperV功能 新建CentOS虚拟机 centos docker安装 由于是新的虚拟机,所以没有docker旧版本的问题,不需要卸载旧 ...