SpringBoot2 参数管理实践之入参出参与校验的方式


Posted in Java/Android onJune 16, 2021

一、参数管理

在编程系统中,为了能写出良好的代码,会根据是各种设计模式、原则、约束等去规范代码,从而提高代码的可读性、复用性、可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思路和封装,这应该是非常高水准。

但是在日常开发中,碍于很多客观因素,很少有时间去不断思考和优化代码,所以只能从实际情况的角度去思考如何构建系统代码,保证以后自己还能读懂自己的代码,在自己的几年编程中,实际会考虑如下几个方面:代码层级管理,命名和注释统一,合理的设计业务数据库,明确参数风格。

这里就来聊一下参数管理,围绕:入参、校验、返参三个方面内容。

如何理解代码规范这个概念:即大多数开发认同,愿意遵守的约束,例如Spring框架和Mvc模式对于工程的管理,《Java开发手册》中对于业务开发的规定,其根本目的都是想避免随着业务发展,代码演变到无法维护的境界。

 

二、接收参数

接收参数方式有很多种,List,Map,Object等等,但是为了明确参数的语义,通常都需要设计参数对象的结构并且遵守一定的规范,例如明确禁止Map接收参数:

Rest风格接收单个ID参数:

@GetMapping("/param/single/{id}")
public String paramSingle (@PathVariable Integer id){
    return "Resp:"+id ;
}

接收多个指定的参数:

@GetMapping("/param/multi")
public String paramMulti (@RequestParam("key") String key, @RequestParam("var") String var){
    return "Resp:"+key+var ;
}

基于Java包装对象入参:

@PostMapping("/param/wrap")
public ParamIn paramWrap (@RequestBody ParamIn paramIn){
    return paramIn ;
}

-- 参数对象实体
public class ParamIn {
    private Integer id ;
    private String key ;
    private String var ;
    private String name ;
}

以上是在开发中常用的几种接参方式,这里通常会遵守下面几个习惯:

  • 参数语义:明确接收参数的作用;
  • 个数限制:参数超过三个使用包装对象;
  • 避免多个接口使用单个包装对象入参;
  • 避免包装对象主体过于复杂;

参数接收并没有很复杂的约束,整体上也比较容易遵守,通常的问题在于处理较大主体对象时,容易产生一个包装对象被多处复用,进而导致对象字段属性很多,这种情况在复杂业务中尤其容易出现,这种对象并不利于web层接口使用,或者很多时候都会在业务层和接口层混用对象;

在业务层封装复杂的BO对象来降低业务管理的复杂度,这是合理常见的操作,可以在web接口层面根据接口功能各自管理入参主体,在业务实现的过程中,再传入BO对象中。

避免复杂的业务包装对象在各个层乱飘,如果多个接口入参都是同一个复杂的对象,很容易让开发人员迷茫。

 

三、响应参数

与参数接收相对应的就是参数响应,参数响应通常具有明确的约束规范:响应主体数据,响应码,描述信息。通常来说就是这样三个核心要素。

响应参数主体:

这里泛型的使用通常用来做主体数据的接收。

public class Resp<T> {

    private int code ;
    private String msg ;
    private T data ;

    public static <T> Resp<T> ok (T data) {
        Resp<T> result = new Resp<>(HttpStatus.OK);
        result.setData(data);
        return result ;
    }

    public Resp (HttpStatus httpStatus) {
        this.code = httpStatus.value();
        this.msg = httpStatus.getReasonPhrase();
    }

    public Resp(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

Code状态码

即接口状态,建议参照并遵守HttpStatus中状态码的描述,这是开发普遍遵守的规范,如果不满足业务需求,在适当自定义部分编码,可以完全自定义一套响应码,但是没太多必要。

Msg描述

描述接口的响应的Msg可能就是:成功或失败,更多的时候是需要处理业务异常的提示信息,例如单号不存在,账号冻结等等,通常需要从业务异常中捕获提示信息,并响应页面,或者入参校验不通过的描述。

Data数据

接口响应的主体数据,不同的业务响应的对象肯定不同,所以这里基于泛型机制接收即可,再以JSON格式响应页面。

参考案例

接口返参:

@PostMapping("/resp/wrap")
public Resp<KeyValue> respWrap (@RequestBody KeyValue keyValue){
    return Resp.ok(keyValue) ;
}

响应格式:

{
   "code": 200,
   "msg": "OK",
   "data": {
       "key": "hello",
       "value": "world"
   }
}

 

四、参数校验

参数接收和响应相对都不是复杂的,比较难处理的就是参数校验:入参约束校验,业务合法性校验,响应参数非空非null校验,等各种场景。

在系统运行过程中,任何参数都不是绝对可靠的,所以参数校验随处可见,不同场景下的参数校验,都有其必要性,但其根本目的都是为了给到请求端提示信息,快速打断流程,快速响应。

 

1、借鉴参考

很多封装思想,设计模式,或者这里说的参数校验,都可以参考现有Java源码或者优秀的框架,这是一个应该具备的基础意识。

Java原生方法之java.lang.Thread线程:

public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();   
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

在Java源码中,大部分都是采用原生的if判断方式,对参数执行校验

Spring框架之org.springframework.util.ClassUtils工具类部分代码:

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
			throws ClassNotFoundException, LinkageError {
		Assert.notNull(name, "Name must not be null");
		Class<?> clazz = resolvePrimitiveClassName(name);
		if (clazz == null) {
			clazz = commonClassCache.get(name);
		}
		if (clazz != null) {
			return clazz;
		}
}

在Spring框架中除了基础的if判断之外,还封装一个org.springframework.util.Assert断言工具类。

 

2、常用校验方式

If判断

@GetMapping("/check/base")
public String baseCheck (@RequestParam("var") String var){
    if (var == null) {
        return var+" is null" ;
    }
    if ("".equals(var)){
        return var+" is empty" ;
    }
    if ("hello".equals(var)){
        return var+" sensitive word " ;
    }
    return var + " through " ;
}

这种判断在代码中很常见,只是一旦遇到校验的主体对象很大,并且在分布式的环境中,需要重复写if判断的话,容易出错是一个方面,对开发人员的耐心考验是另一个方面。

Valid组件

在早几年的时候,比较流行的常用校验组件Hibernate-Validator,后来兴起的Validation-Api,据说是参考前者实现,不过这并不重要,二者都简化了对JavaBean的校验机制。

基于注解的方式,标记Java对象的字段属性,并设定如果校验失败的提示信息。

public class JavaValid {

    @NotNull(message="ID不能为空")
    private Integer id ;

    @Email(message="邮箱格式异常")
    private String email ;

    @NotEmpty(message = "字段不能为空")
    @Size(min = 2,max = 10,message = "字段长度不合理")
    private String data ;
}

校验结果打印:

public class JavaValidTest {

    private static Validator validator ;

    @BeforeClass
    public static void beforeBuild (){
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    @Test
    public void checkValid (){
        JavaValid valid = new JavaValid(null,"email","data") ;
        Set<ConstraintViolation<JavaValid>> validateInfo = validator.validate(valid) ;
        // 打印校验结果
        validateInfo.stream().forEach(validObj -> {
            System.out.println("validateInfo:"+validObj.getMessage());
        });
    }
}

接口使用:

@PostMapping("/java/valid")
public JavaValid javaValid (@RequestBody @Valid JavaValid javaValid,BindingResult errorMsg){
    if (errorMsg.hasErrors()){
        List<ObjectError> objectErrors = errorMsg.getAllErrors() ;
        objectErrors.stream().forEach(objectError -> {
            logger.info("CheckRes:{}",objectError.getDefaultMessage());
        });
    }
    return javaValid ;
}

这种校验机制基于注解方式,可以大幅度简化普通的入参校验,但是对业务参数的合法校验并不适应,例如常见的ID不存在,状态拦截等。

Assert断言

关于Assert断言方式,起初是在单元测试中常见,后来在各种优秀的框架中开始常见,例如Spring、Mybatis等,然后就开始出现在业务代码中:

public class AssertTest {
    private String varObject ;
    @Before
    public void before (){
        varObject = RandomUtil.randomString(6) ;
    }

    @Test
    public void testEquals (){
        Assert.assertEquals(varObject+"不匹配",varObject,RandomUtil.randomString(6));
    }
    @Test
    public void testEmpty (){
        Assert.assertTrue(StrUtil.isNotEmpty(varObject));
        Assert.assertFalse(varObject+" not empty",StrUtil.isNotEmpty(varObject));
    }
    @Test
    public void testArray (){
        /*
            数组元素不相等: arrays first differed at element [1];
            Expected :u08
            Actual   :mwm
         */
        String var = RandomUtil.randomString(5) ;
        String[] arrOne = new String[]{var,RandomUtil.randomString(3)} ;
        String[] arrTwo = new String[]{var,RandomUtil.randomString(3)} ;
        Assert.assertArrayEquals("数组元素不相等",arrOne,arrTwo);
    }
}

Assert断言,可以替换传统的if判断,大量减少参数校验的代码行数,提高程序的可读性,这种风格是目前比较流行的方式。

 

五、源代码地址

GitHub·地址https://github.com/cicadasmile/middle-ware-parentGitEE·

地址https://gitee.com/cicadasmile/middle-ware-parent

以上就是SpringBoot2 参数管理实践,入参出参与校验的详细内容,更多关于SpringBoot2 参数校验的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
浅谈Java实现分布式事务的三种方案
Jun 11 Java/Android
Java实现斗地主之洗牌发牌
Jun 14 Java/Android
JVM入门之类加载与字节码技术(类加载与类的加载器)
Jun 15 Java/Android
使用@Value值注入及配置文件组件扫描
Jul 09 Java/Android
Java spring定时任务详解
Oct 05 Java/Android
Java 在生活中的 10 大应用
Nov 02 Java/Android
Java 在线考试云平台的实现
Nov 23 Java/Android
Java实现经典游戏泡泡堂的示例代码
Apr 04 Java/Android
Android存储中最基本的文件存储方式
Apr 30 Java/Android
Ubuntu18.04下QT开发Android无法连接设备问题解决实现
Jun 01 Java/Android
Spring Cloud OAuth2实现自定义token返回格式
Jun 25 Java/Android
Java+swing实现抖音上的表白程序详解
Jun 25 Java/Android
SpringBoot生成License的实现示例
Springboot如何使用logback实现多环境配置?
解决tk mapper 通用mapper的bug问题
一篇带你入门Java垃圾回收器
Java实现简易的分词器功能
Java用自带的Image IO给图片添加水印
java Nio使用NioSocket客户端与服务端交互实现方式
You might like
咖啡知识 咖啡养豆要养多久 排气又是什么
2021/03/06 新手入门
如何去掉文章里的 html 语法
2006/10/09 PHP
解析func_num_args与func_get_args函数的使用
2013/06/24 PHP
PHP中使用sleep造成mysql读取失败的案例和解决方法
2014/08/21 PHP
PHP生成各种随机验证码的方法总结【附demo源码】
2017/06/05 PHP
在IE模态窗口中自由查看HTML源码的方法
2007/03/08 Javascript
一个js实现的所谓的滑动门
2007/05/23 Javascript
JS弹出窗口代码大全(详细整理)
2012/12/21 Javascript
获取表单控件原始(初始)值的方法
2013/08/21 Javascript
AngularJs  E2E Testing 详解
2016/09/02 Javascript
jQuery实现页面下拉100像素出现悬浮窗口的方法
2016/09/05 Javascript
JS+CSS实现下拉刷新/上拉加载插件
2017/03/31 Javascript
[02:09]EHOME夺得首届辉夜杯冠军—现场颁奖仪式
2015/12/28 DOTA
python迭代器的使用方法实例
2013/11/21 Python
Python Web框架Pylons中使用MongoDB的例子
2013/12/03 Python
Python中的错误和异常处理简单操作示例【try-except用法】
2017/07/25 Python
python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解
2017/11/24 Python
Python语言描述机器学习之Logistic回归算法
2017/12/21 Python
使用python编写简单的小程序编译成exe跑在win10上
2018/01/15 Python
学习Python3 Dlib19.7进行人脸面部识别
2018/01/24 Python
Pyinstaller打包.py生成.exe的方法和报错总结
2019/04/02 Python
PyCharm+Qt Designer+PyUIC安装配置教程详解
2019/06/13 Python
如何用Python制作微信好友个性签名词云图
2019/06/28 Python
python判断单向链表是否包括环,若包含则计算环入口的节点实例分析
2019/10/23 Python
python交互模式基础知识点学习
2020/06/18 Python
pycharm激活码2020最新分享适用pycharm2020最新版亲测可用
2020/11/22 Python
python-地图可视化组件folium的操作
2020/12/14 Python
详解基于canvas的视频遮罩插件
2018/01/04 HTML / CSS
eDreams澳大利亚:预订机票、酒店和度假产品
2017/04/19 全球购物
马来西亚在线时尚女装商店:KEI MAG
2017/09/28 全球购物
Rakuten Kobo台湾:电子书、eReaders和Reading应用程式
2017/11/24 全球购物
中专生求职自荐信范文
2013/12/22 职场文书
技术合作协议书范本
2014/04/18 职场文书
2015新年联欢晚会开场白
2014/12/14 职场文书
三方合作意向书范本
2015/05/09 职场文书
nginx结合openssl实现https的方法
2021/07/25 Servers