引言

编写接口时,常用的参数校验使用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);
//接口处理业务代码
...
}

知识点

涉及到的知识点

  1. 建造者模式
  2. 链式调用
  3. 枚举
  4. 异常
  5. 继承与多态

拓展所需知识点

  1. 委托模式

使用前准备

自定义异常

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)的更多相关文章

  1. Spring Boot 之:接口参数校验

    Spring Boot 之:接口参数校验,学习资料 网址 SpringBoot(八) JSR-303 数据验证(写的比较好) https://qq343509740.gitee.io/2018/07/ ...

  2. Spring Boot实现通用的接口参数校验

    Spring Boot实现通用的接口参数校验 Harries Blog™ 2018-05-10 2418 阅读 http ACE Spring App API https AOP apache IDE ...

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

    本文介绍基于Spring Boot和JDK8编写一个AOP,结合自定义注解实现通用的接口参数校验. 缘由 目前参数校验常用的方法是在实体类上添加注解,但对于不同的方法,所应用的校验规则也是不一样的,例 ...

  4. springboot 接口参数校验

    前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...

  5. 接口参数校验之@Valid与BindingResult

    接口方法往往需要对入参做一些校验,从而判断入参是否合格,而javax.validation包为我们提供了一些常用的参数校验注解,使用起来很方便. 下面这个示例是检验入参对象中的password是否为空 ...

  6. 【快学springboot】4.接口参数校验

    前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...

  7. 使用hibernate validate做参数校验

    1.为什么使用hibernate validate ​ 在开发http接口的时候,参数校验是必须有的一个环节,当参数校验较少的时候,一般是直接按照校验条件做校验,校验不通过,返回错误信息.比如以下校验 ...

  8. Spring Boot 参数校验

    1.背景介绍 开发过程中,后台的参数校验是必不可少的,所以经常会看到类似下面这样的代码 这样写并没有什么错,还挺工整的,只是看起来不是很优雅而已. 接下来,用Validation来改写这段 2.Spr ...

  9. spring注解式参数校验

    很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者返回异常时的校验信息,在代码中相当冗长,今天我们就来学习spring注解式参数校验. 其实就是:hibernate的validator. 开始啦. ...

随机推荐

  1. WMware中Ubuntu系统安装VMware tools

    在VMware的虚拟机中安装完ubuntu之后,继续安装VMware tools. 一般情况下,这时都有光驱的图标,点开就能找到"VMwareTools-10.0.10-4301679.ta ...

  2. Eclipse Mac OS 安装 Subversion插件subclipse 缺失JavaHL解决方案

    安装 SVN 插件 subclipse 时可能遇到问题 subclipse 安装完成后,当我们选择使用 的时候还是会提示:javaHL not available, SVN接口选择 client:选择 ...

  3. Python自动化运维一之psutil

    1.1系统性能信息模块psutil 1.1.1下载安装psutil 1.   wget https://pypi.python.org/packages/source/p/psutil/psutil- ...

  4. WeChatSampleBuilder V2.0 使用教程(网页版+桌面版)

    为了方便开发者可以快速搭建一个最小化所需模块的 Senparc.Weixin SDK Sample 项目,我们于 2018 年 11 月发布了首个 WeChatSampleBuilder 的版本,受到 ...

  5. 图论--LCA--Tarjan(离线)

    * * 给出一颗有向树,Q个查询 * 输出查询结果中每个点出现次数 * 复杂度O(n + Q); */ const int MAXN = 1010; const int MAXQ = 500010; ...

  6. codeforce 270B Multithreading

    B. Multithreading Emuskald is addicted to Codeforces, and keeps refreshing the main page not to miss ...

  7. 题目分享L

    题意:n个人围成一个环,每个人初始有一些金币,每个人可以把金币递给相邻的人,问最少传递多少金币使每个人金币数相同? 分析:首先在保证最优的情况下不可能会出现相邻的两个人互相送金币,因为这样他们公共的部 ...

  8. linux命令之df dh

    df -h, --human-readable 查看磁盘空间占用情况 df -h du -h, --human-readable -s, --summarize 查看文件大小 du -h test.t ...

  9. # C#学习笔记(一)——准备工作

    C#学习笔记(一)--准备工作 目录 C#学习笔记(一)--准备工作 1.1 下载安装.NET框架 1.2 创建源代码 1.3 一些基本名称 1.4 简单的命名建议 1.1 下载安装.NET框架 .N ...

  10. FPGA自计数六位共阳极数码管动态显示2(调用task的方法)

    `timescale 1ns/1ps module adc_dis( clk , rst_n , sm_seg , sm_bit ); input clk;//50HZ input rst_n; :] ...