接口参数校验(不使用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. 开始啦. ...
随机推荐
- Vue项目开发流程(自用)
一.配置开发环境 1.1 安装Node.js npm集成在Node中,检查是否安装完成:node -v 1.2 安装cnpm(淘宝镜像) npm install -g cnpm,检查安装是否完成:cn ...
- LightOJ 1287 Where to Run(期望)
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1287 题意:给定一个n个点的无向图(0到n-1),你开始在0.你开始遍历这个图 ...
- CF1324F Maximum White Subtree——换根dp
换根dp,一般用来解决在无根树上,需要以每个节点为根跑一边dfs的dp问题 我们做两遍dfs 先钦定任意一个点为根 第一遍,算出\(f_i\)表示\(i\)的子树产生的答案,这里,子树指的是以我们钦定 ...
- [转载] IE8+兼容小结
本文分享下我在项目中积累的IE8+兼容性问题的解决方法.根据我的实践经验,如果你在写HTML/CSS时候是按照W3C推荐的方式写的,然后下面的几点都关注过,那么基本上很大一部分IE8+兼容性问题都OK ...
- JSP+Spring+SpringMVC+Hibernate+Mysql实现的校园失物招领网站
项目简介 项目来源于:https://github.com/wenlongup/LostAndFound 因原github仓库无数据库文件,经过本人修改,现将该仓库重新上传至个人gitee仓库. ht ...
- Spring Cloud学习 之 Spring Cloud Hystrix(使用详解)
文章目录 创建请求命令: 定义服务降级: 异常处理: 异常传播: 异常获取: 命令名称,分组以及线程池划分: 创建请求命令: Hystrix命令就是我们之前说的HystrixCommand,它用来 ...
- 实时(RTC)时钟,系统时钟和CPU时钟
最近在学stm32的时候看到RTC时钟和系统时钟,不知道区别在哪里,于是上网查了一下. 实时时钟:RTC时钟,用于提供年.月.日.时.分.秒和星期等的实时时间信息,由后备电池供电,当你晚上关闭系统和早 ...
- 软路由OpenWrt(LEDE)2020.4.6编译 UnPnP+NAS+多拨+网盘+DNS优化
近期更新:2020.04.06编译-基于OpenWrt R2020.3.19版本,源码截止2020.04.06. 2020.04.06更新记录: 以软件包形式提供ServerChan(微信推送) ...
- C语言进阶_goto与void解析
世间真正温煦的春色,都熨帖者大地,潜伏在深谷. 一.简介 goto与void是两个不常用的关键字,C语言中的Goto关键字传承字汇编语言,使用方法也与汇编语言相同.void是类型关键字,可修饰函数和变 ...
- CF-292D Connected Components 并查集 好题
D. Connected Components 题意 现在有n个点,m条编号为1-m的无向边,给出k个询问,每个询问给出区间[l,r],让输出删除标号为l-r的边后还有几个连通块? 思路 去除编号为[ ...