接口参数校验(不使用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. 开始啦. ...
随机推荐
- material UI中withStyles和makeStyles的区别
在material UI中,withStyles和makeStyles是经常使用的两个用于封装样式的函数.对于刚使用material UI的开发者而言,可能不太清楚这两者的区别. 本文简要探究 ...
- Hard filters (by GATK)
Filter Symbol T. Definition QualByDepth QD 2.0 The variant confidence (from the QUAL field) divided ...
- inotifywait实现文件监控
应用场景文件监控可以配合rsync实现文件自动同步,例如监听某个目录,当文件变化时,使用rsync命令将变化的文件同步.(可用于代码自动发布) 安装noitify下载地址:http://github. ...
- Codeforce 1098-A
A. Sum in the tree Mitya has a rooted tree with nn vertices indexed from 11 to nn, where the root ...
- The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 D Carneginon
Carneginon was a chic bard. But when he was young, he was frivolous and had joined many gangs. Recen ...
- 使用jQuery完成课工场论坛列表
1.点击我要发帖 2.显示出form表单,然后我们填入标题和选择板块 3.点击发布,隐藏表单,发帖列表中出现随机头像,刚才填入的标题和板块显示在列表中,其中还显示出了发布消息的时间 4.再一次的点击我 ...
- Eating Everything Efficiently(反向dp)
传送门 取最大值即可.用拓扑,dfs都可以实现 #include <bits/stdc++.h> using namespace std; const int maxn=500009; i ...
- Qt读写xml文件
写xml <root> <element> <sub id=-1></sub> </element> </root> //添加x ...
- python是如何进行参数传递的?
在分析python的参数传递是如何进行的之前,我们需要先来了解一下,python变量和赋值的基本原理,这样有助于我们更好的理解参数传递. python变量以及赋值 数值 从几行代码开始 In [1]: ...
- ReactNative报错:Can't find variable: __fbBatchedBridge
最近开始研究ReactNative,首先根据网上教程 http://www.codeceo.com/article/windows-react-native-android.html 一步一步来.完成 ...