接口参数校验(不使用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. 开始啦. ...
随机推荐
- nginx平滑升级、在线添加模块(tengine 动态加载模块)
http://www.orzace.com/how-to-upgrade-nginx/ 下面是nginx替换成tengine再加上lua 模块,(tengine-2.0.1版本暂时无法动态加载lua模 ...
- 【批处理】TXT文件批量转HTML文件工具
说到批量转html文件,相信喜欢看小说或经常制作电子书和教程的朋友应该很熟悉.因为,我们每次都会面临成千上万的txt文件,要将其转换为能正确显示的html文件是很麻烦的.当然,现在有很多的软件也可以实 ...
- GCD-Euclidean Algorithm
求解两个正整数的最大公约数(Greatest Common Devisor),可以采用循环进行遍历,不过效率很低.所以引入欧几里得算法(Euclid's algorithm). 欧几里得算法基于GCD ...
- The 2019 Asia Nanchang First Round Online Programming Contest B Fire-Fighting Hero(阅读理解)
This is an era of team success, but also an era of heroes. Throughout the ages, there have been nume ...
- python模块之time and datetime
time # python3 # coding = utf-8 import time timestamp = time.time() print('timestamp:%s, type: %s' % ...
- 【杂谈】Disruptor——RingBuffer问题整理(一)
纯CAS为啥比加锁要快? 同样是修改数据,一个采用加锁的方式保证原子性,一个采用CAS的方式保证原子性. 都是能够达到目的的,但是常用的锁(例如显式的Lock和隐式的synchonized),都会把获 ...
- Java的Object.wait(long)在等待时间过去后会继续往后执行吗
Java的Object.wait(long)在等待时间过去后会继续往后执行吗 Object.wait(long)方法相比于wait,多了个等待时长,那么当等待时长过去后,线程会继续往下执行吗? 单个线 ...
- LFU C# 实现
周六早上 做了下力扣的LRU 题目 后面接着看了LFU 缓存 难度提高了不少 首先 先说下 这2着 的差别把 LRU :最近 最少使用算法(Least Recently Used).LRU 是 ...
- Vue + Element-ui实现后台管理系统(4)---封装一个ECharts组件的一点思路
封装一个ECharts组件的一点思路 有关后台管理系统之前写过三遍博客,看这篇之前最好先看下这三篇博客.另外这里只展示关键部分代码,项目代码放在github上: mall-manage-system ...
- Vue + Element-ui实现后台管理系统(1) --- 总述
总述 一.项目效果 整体效果 登陆页 后台首页 用户管理页 说明 这里所有的数据都不是直接通过后端获取的, 而是通过Mock这个工具来模拟后端返回的接口数据. 附上github地址: mall-ma ...