购物网站开发背景及意义,上上海网站设计建设,中国建设机械职业教育网证书查询,务川县建设局官方网站简介#xff1a; 希望本文可以帮助到大家#xff0c;可以用一种优雅方式接入参数校验#xff0c;保护系统解放自身#xff0c;从你我做起#xff01; 作者 | 中野 来源 | 阿里技术公众号 一 不厌其烦的 if else?
参数校验#xff0c;为了保护自己的代码#xff0c;一般…简介 希望本文可以帮助到大家可以用一种优雅方式接入参数校验保护系统解放自身从你我做起 作者 | 中野 来源 | 阿里技术公众号 一 不厌其烦的 if else?
参数校验为了保护自己的代码一般都会在开发中假设所有的参数都是不可靠的。针对所有的参数校验场景自己一次进行判断及错误信息的提示。
例如
if(a.size 10 a.size 100){Result result Reuslt.fail(非法参数size , 请检查输入!) ;return result;
}if(xxx)return xxx ;
还有一种case重一点的业务参数校验有时候也会被不厌其烦地校验散落在各个子系统或者系统的各处模块代码中。
例如
if(!validItem(itemId)){Result result Reuslt.fail(不存在的商品id , 请检查输入!) ;return result;
}
private boolean validItem(itemId){// RPC getItem// 是否空判断return item ! null;
}
针对以上的场景本文探讨一下如何优雅地在业务系统中做参数校验分享构建通用校验模块的一些实践。
二 业内框架 hibernate validator
1 简介
JSR提供了一套Bean校验规范的API维护在包javax.validation.constraints下。该规范使用属性或者方法参数或者类上的一套简洁易用的注解来做参数校验。开发者在开发过程中仅需在需要校验的地方加上形如NotNull, NotEmpty , Email的注解就可以将参数校验的重任委托给一些第三方校验框架来处理。
引自网络
JSR-303 是 JAVA EE 6 中的一项子规范叫做 Bean Validation官方参考实现是Hibernate Validator。 此实现与 Hibernate ORM 没有任何关系。JSR 303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303可以在控制器中对表单提交的数据方便地验证。 注可以使用注解的方式进行验证。 接入validation api及hibernate validator后做普通的参数校验简单到不行 Hibernate Validator支持了一系列的如非空 有效邮箱正则表达式是否匹配等一系列基础校验支持
EmailNotNullPatternAssertFalse......
而像业务系统中常见的校验Hibernate Validator是无法支持的例如校验订单号是否有效订单上的商品id是否真实有效这种校验Hibernate也留了口子可以自行定义注解同时自行定义校验逻辑后依赖SPI机制注册到Hibernate Validator中即可。
如何自定义业务参数校验API及其校验实现可以参考官方文档不再赘述Hibernate Validator的用法。
2 实现原理
可以想下如果自己做一套支持JSR303 bean校验规范的校验框架我们会如何实现。
其实无非是读取class元数据获取bean类上的所有带有校验注解的属性在每次需要校验对象的时候拿到对象对应属性的值来与其上的所有校验注解来执行校验实现逻辑然后收集所有不通过的信息。
hibernate validator的实现核心原理也是如此 上图仅展示一些Hibernate validator的核心组件实际上有非常多的细节不在此赘述有兴趣了解全流程的同学可以自行debug一下并不是非常复杂。
校验的过程
配置Hibernate Validator把所有相关的非懒加载的核心组件都进行初始化依赖java的SPI机制支持自定义validator进行校验先进行class数据解析然后获取对应属性的对应validator进行校验 最后通过MessageInterpolator组件进行校验错误信息的提取。依赖java的ResourceBundle机制支持校验信息多语言。
三 优雅实践
基于hibernate validator怎么可以做一些优雅实践呢
hibernate validator仅是bean校验框架 可能还需要做一些适配才可以让我们在业务系统开发中下面分享一下一些开发实践核心追求的是业务逻辑与参数校验逻辑完全解耦合常用的业务参数校验逻辑可以在多套业务系统中被复用以及统一维护所有的校验错误信息。
概要图 RPC与WEB系统部署架构图 拦截所有请求可以基于RPC filter和Spring MVC的HandlerInterceptor来实现RPC请求和HTTP请求的拦截 拦截器中使用validator校验参数失败的话直接设置失败信息快速返回。统一参数校验包纯粹的校验API所有校验以注解形式做抽象支持简单复用 , 形如NotNull , ExistItem , ExistBarcode的作用于参数上的注解 这个可以复用JSR校验规范来实现。统一参数校验的实现(validator) 所有的校验注解对应的校验逻辑实现以统一maven依赖形式提供 和校验API一一对应。hibernate validator进行扩展校验错误信息解析统一维护于配置中心接入在配置中心可以在运行时动态修改以本地文件形式存储校验提示信息也并无不可只是维护起来复杂麻烦。维护简单易用的starter开箱即用支持所有业务系统快速接入。
一些代码实现需要自取
RPC filter ResourceBundle
// 使用自定义的配置中心信息源 初始化validator
public static Validator validator;static {HibernateValidatorConfiguration configure Validation.byProvider(HibernateValidator.class).configure();ResourceBundleLocator defaultResourceBundleLocator configure.getDefaultResourceBundleLocator();ResourceBundleLocator myResourceBundleLocator new MyResourceBundleLocator(defaultResourceBundleLocator);configure.messageInterpolator(new ResourceBundleMessageInterpolator(myResourceBundleLocator));configure.enableTraversableResolverResultCache(false);validator configure.buildValidatorFactory().getValidator();}// RPC服务校验失败时候直接mockresponse快速返回response中设置errorMsgString message collectValidateMessage(args);if (StringUtils.isNotEmpty(message)) {// fail fastRPCResult rpcResult new RPCResult();rpcResult.setHsfResponse(new HSFResponse());rpcResult.setAppResponse(mockResponse(invocation, message));SettableFutureRPCResult defaultRPCFuture Futures.createSettableFuture();defaultRPCFuture.set(rpcResult);return defaultRPCFuture;// 配置中心的ResourceBundlepublic class DiamondResourceBundle extends ResourceBundle {private static final Properties properties new Properties();public DiamondResourceBundle() {try {init();} catch (IOException e) {log.error(初始化diamond数据失败 , e);}}private void init() throws IOException {// load onceloadConfig(Diamond.getConfig(DATA_ID, GROUP_ID, 5000));// add listenerDiamond.addListener(DATA_ID, GROUP_ID, new ManagerListener() {Overridepublic Executor getExecutor() {return pushExecutor;}Overridepublic void receiveConfigInfo(String configInfo) {log.error(receive config : {} , configInfo);// load configloadConfig(configInfo);clearCache();}});}}四 优秀框架校验实现
实际上参数校验是所有coder都会遇到的问题如何更加优雅地解决参数校验的问题呢
列举一些框架一起学习一下他们如何做参数校验
1 Spring
Spring 没有使用任何的参数校验框架使用其维护的Assert工具类常用的参数异常来做参数校验。所有的参数校验都是在编码时候书写的。 都是使用Assert.notNull , Assert.notEmpty等来做参数校验。
Spring主要还是面向开发的框架出现参数异常其信息是面向开发者的与我们这种面向用户的校验存在区别。不会出现业务参数校验失败的情况人肉校验简单参数也无可厚非。
而且Spring是作用在应用启动时候的框架对用户理论上无影响。
2 Feign 看了一些如Feign的基础框架都是手动校验的参数不复杂 这里不一一列举了。
基础框架和业务系统有根本上的差异基础框架是面向开发人员的框架大部分都是在系统部署时候启动校验有异常的话都是直接抛出开发人员可以根据错误信息及时排查。
而业务系统敲代码嗖嗖嗖的敲完了业务逻辑如果参数传的有误可能会直接导致系统不可用。
这种情况可能由于接口调用方没有使用准确参数前端没有做参数校验等等但无论如何我们必须保证自身系统是稳定可靠的尤其需要使我们的系统远离“外部”的无效数据留意每一个参数的可靠性及边界情况。
五 总结
最后分享一下防御性编程的一些原则希望你我一起严格按照原则来保护线上系统。
引自网络 Steve McConnell 的经典编程之书——《Code Complete》用一个短篇解释了防御性编程的一些基本规则 保护你的代码远离来自“外部”的无效数据无论这个“外部”的概念被定位为什么。它可以是来自于外部系统、用户、文件的数据也可以是模块/组件以外的数据由你决定。树立“路障”、“安全区”或“信任边界”——在边界之外的一切都是危险的界限之内的所有都是安全的。关于“路障”代码需要验证所有的输入数据检查所有输入参数的类型、长度和值域是否正确。还要加倍检查限制和界限。当我们检查出错误数据后还需要决定如何处理它。防御性编程不会掩盖错误也不会隐藏bug。这需要在健壮性如果问题可以处理那就继续运行和正确性不返回不准确的结果之间做权衡。选择好策略来应对错误数据返回错误就马上停止返回中性值就替换数据值……确保策略明确且一贯。不要将代码外部的函数调用或方法调用想得太过美好。请确保你调用外部的API和库之前理解并测试了错误。至少在开发和测试阶段要使用断言记录假设并高亮“不可能”的条件。这在大型系统中显得尤为重要因为随着时间的推移将会有不同的程序员用高度可靠的代码来维护这些大型系统。添加诊断代码智能地记录和跟踪以帮助解释在运行时发生的事情尤其是当你遇到问题的时候。标准化的错误处理。想好如何处理“正常错误”、“预期错误”以及警告并对此习以为常。只有当你真的需要的时候才使用异常处理并确保你得彻底理解该编程语言的异常处理程序。搞起来希望本文可以帮助到大家可以用一种优雅方式接入参数校验保护系统解放自身从你我做起
原文链接 本文为阿里云原创内容未经允许不得转载。