前后端分离的思考与实践(三)
Midway-ModelProxy — 轻量级的接口配置建模框架
前言
使用Node做前后端分离的开发模式带来了一些性能及开发流程上的优势(见《前后端分离的思考与实践 一》), 但同时也面临不少挑战。在淘宝复杂的业务及技术架构下,后端必须依赖Java搭建基础架构,同时提供相关业务接口供前端使用。Node在整个环境中最重要 的工作之一就是代理这些业务接口,以方便前端(Node端和浏览器端)整合数据做页面渲染。如何做好代理工作,使得前后端开发分离之后,仍然可以在流程上 无缝衔接,是我们需要考虑的问题。本文将就该问题做相关探讨,并提出解决方案。

由于后端提供的接口方式可能多种多样,同时开发人员在编写Node端代码访问这些接口的方式也有可能多种多样。如果我们在接口访问方式及使用上不做统一架构处理,则会带来以下一些问题:
1. 每一个开发人员使用各自的代码风格编写接口访问代码,造成工程目录及编码风格混乱,维护相对困难。
2. 每一个开发人员编写自己的mock数据方式,开发完毕之后,需要手工修改代码移除mock。
3. 每一个开发人员为了实现接口的不同环境切换(日常,预发,线上),可能各自维护了一些配置文件。
4. 数据接口调用方式无法被各个业务model非常方便地复用。
5. 对于数据接口的描述约定散落在代码的各个角落,有可能跟后端人员约定的接口文档不一致。
6. 整个项目分离开发之后,对于接口的联调或者测试回归成本依然很高,需要涉及到每一个接口提供者和使用者。
于是我们希望有这样一个框架,通过该框架提供的机制去描述工程项目中依赖的所有外部接口,对他们进行统一管理,同时提供灵活的接口建模及调用方式, 并且提供便捷的线上环境和生产环境切换方法,使前后端开发无缝结合。ModelProxy就是满足这样要求的轻量级框架,它是Midway Framework 核心构件之一,也可以单独使用。使用ModelProxy可以带来如下优点:
1. 不同的开发者对于接口访问代码编写方式统一,含义清晰,降低维护难度。
2. 框架内部采用工厂+单例模式,实现接口一次配置多次复用。并且开发者可以随意定制组装自己的业务Model(依赖注入)。
3. 可以非常方便地实现线上,日常,预发环境的切换。
4. 内置river-mock和mockjs等mock引擎,提供mock数据非常方便。
5. 使用接口配置文件,对接口的依赖描述做统一的管理,避免散落在各个代码之中。
6. 支持浏览器端共享Model,浏览器端可以使用它做前端数据渲染。整个代理过程对浏览器透明。
7. 接口配置文件本身是结构化的描述文档,可以使用river工具集合,自动生成文档。也可使用它做相关自动化接口测试,使整个开发过程形成一个闭环。
ModelProxy工作原理图及相关开发过程图览

在上图中,开发者首先需要将工程项目中所有依赖的后端接口描述,按照指定的json格式,写入interface.json配置文件。必要时,需要 对每个接口编写一个规则文件,也即图中interface rules部分。该规则文件用于在开发阶段mock数据或者在联调阶段使用River工具集去验证接口。规则文件的内容取决于采用哪一种mock引擎(比 如 mockjs, river-mock 等等)。配置完成之后,即可在代码中按照自己的需求创建自己的业务model。
下面是一个简单的例子:
【例一】
第一步 在工程目录中创建接口配置文件interface.json, 并在其中添加主搜接口json定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "title": "pad淘宝项目数据接口集合定义", "version": "1.0.0", "engine": "mockjs", "rulebase": "./interfaceRules/", "status": "online", "interfaces": [ { "name": "主搜索接口", "id": "Search.getItems", "urls": { "online": "http://s.m.taobao.com/client/search.do" } } ] } |
第二步 在代码中创建并使用model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 引入模块 var ModelProxy = require( 'modelproxy' ); // 全局初始化引入接口配置文件 (注意:初始化工作有且只有一次) ModelProxy.init( './interface.json' ); // 创建model 更多创建模式请参后文 var searchModel = new ModelProxy( { searchItems: 'Search.getItems' // 自定义方法名: 配置文件中的定义的接口ID } ); // 使用model, 注意: 调用方法所需要的参数即为实际接口所需要的参数。 searchModel.searchItems( { q: 'iphone6' } ) // !注意 必须调用 done 方法指定回调函数,来取得上面异步调用searchItems获得的数据! .done( function( data ) { console.log( data ); } ) .error( function( err ) { console.log( err ); } ); |
ModelProxy的功能丰富性在于它支持各种形式的profile以创建需要业务model:
使用接口ID创建>生成的对象会取ID最后’.’号后面的单词作为方法名
1 | ModelProxy.create( 'Search.getItem' ); |
使用键值JSON对象>自定义方法名: 接口ID
1 2 3 4 | ModelProxy.create( { getName: 'Session.getUserName', getMyCarts: 'Cart.getCarts' } ); |
使用数组形式>取最后 . 号后面的单词作为方法名
下例中生成的方法调用名依次为: Cart_getItem, getItem, suggest, getName
1 | ModelProxy.create( [ 'Cart.getItem', 'Search.getItem', 'Search.suggest', 'Session.User.getName' ] ); |
前缀形式>所有满足前缀的接口ID会被引入对象,并取其后半部分作为方法名
1 | ModelProxy.create( 'Search.*' ); |
同时,使用这些Model,你可以很轻易地实现合并请求或者依赖请求,并做相关模板渲染
【例二】 合并请求
1 2 3 4 5 6 7 8 9 10 | var model = new ModelProxy( 'Search.*' ); // 合并请求 (下面调用的model方法除done之外,皆为配置接口id时指定) model.suggest( { q: '女' } ) .list( { keyword: 'iphone6' } ) .getNav( { key: '流行服装' } ) .done( function( data1, data2, data3 ) { // 参数顺序与方法调用顺序一致 console.log( data1, data2, data3 ); } ); |
【例三】 依赖请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var model = new ModelProxy( { getUser: 'Session.getUser', getMyOrderList: 'Order.getOrder' } ); // 先获得用户id,然后再根据id号获得订单列表 model.getUser( { sid: 'fdkaldjfgsakls0322yf8' } ) .done( function( data ) { var uid = data.uid; // 二次数据请求依赖第一次取得的id号 this.getMyOrderList( { id: uid } ) .done( function( data ) { console.log( data ); } ); } ); |
此外ModelProxy不仅在Node端可以使用,也可以在浏览器端使用。只需要在页面中引入官方包提供的modelproxy-client.js即可。
【例四】浏览器端使用ModelProxy
1 2 | <!-- 引入modelproxy模块,该模块本身是由KISSY封装的标准模块--> <script src="modelproxy-client.js" ></script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <script type="text/javascript"> KISSY.use( "modelproxy", function( S, ModelProxy ) { // !配置基础路径,该路径与第二步中配置的拦截路径一致! // 且全局配置有且只有一次! ModelProxy.configBase( '/model/' ); // 创建model var searchModel = ModelProxy.create( 'Search.*' ); searchModel .list( { q: 'ihpone6' } ) .list( { q: '冲锋衣' } ) .suggest( { q: 'i' } ) .getNav( { q: '滑板' } ) .done( function( data1, data2, data3, data4 ) { console.log( { "list_ihpone6": data1, "list_冲锋衣": data2, "suggest_i": data3, "getNav_滑板": data4 } ); } ); } ); </script> |
同时,ModelProxy可以配合Midway另一核心组件Midway-XTPL一起使用,实现数据和模板以及相关渲染过程在浏览器端和服务器端的全共享。关于ModelProxy的详细教程及文档请移步https://github.com/purejs/modelproxy
总结
ModelProxy以一种配置化的轻量级框架存在,提供友好的接口model组装及使用方式,同时很好的解决前后端开发模式分离中的接口使用规范 问题。在整个项目开发过程中,接口始终只需要定义描述一次,前端开发人员即可引用,同时使用River工具自动生成文档,形成与后端开发人员的契约,并做 相关自动化测试,极大地优化了整个软件工程开发过程。
【注】River 是阿里集团研发的前后端统一接口规范及相关工具集合的统称
转载于:https://blog.51cto.com/h2appy/1851564
相关文章:

替换HTML代码
将一些HTML替换掉 eg:strContentstrContent.Replace("&","&");strContentstrContent.Replace("","");strContentstrContent.Replace("<","<");strContentstrContent.Replace(">&quo…

什么是线程安全?
http://baike.baidu.com/view/1298606.htm 线程安全 目录 什么是线程安全?举例线程安全性线程安全程度不可变线程安全有条件的线程安全线程兼容线程对立什么是线程安全?举例线程安全性线程安全程度不可变线程安全有条件的线程安全线程兼容线程对立展开编…
中国开源大爆发进行时,你没掉队吧?
作者 | 陈利鑫头图 | CSDN 下载自东方 IC出品 | CSDN(ID:CSDNnews)从开源(Open Source)一词提出到如今,开源的概念越来越成熟,作为一种创造及协作模式,开源已经不仅仅局限于软件技术…

eclipse设置
2019独角兽企业重金招聘Python工程师标准>>> eclipse 下载 地址 http://www.eclipse.org/downloads/packages/release/Ganymede/SR2 设置 jdk 参数 jre definition vm arguments -Xms800m -Xmx800m -XX:MaxNewSize256m -XX:MaxPermSize256m -Dfile.encod…
吊打一切:YOLOv4的tricks汇总
来源 | AI算法与图像处理(ID:AI_study)即使是目标检测在过去几年开始成熟,竞争仍然很激烈。如下所示,YOLOv4声称拥有最先进的精度,同时保持高处理帧速率。它在 MS COCO数据集上,使用Tesla V100以接近65 FPS…

如何播放RM文件?
<EMBED SRC"MY_LIFE.RPM" WIDTH300 HEIGHT134 >Tip:SRC是可以省略的,当mime Type 参数如下时:<EMBED ...,TYPE"audio/x-pn-realaudio-plugin",...>,这样做会产生不可预料的结果,因此强烈推荐你包…

mongodb地理位置索引实现原理
地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一。我们知道,通常的数据库索引结构是B Tree,如何将地理位置转化为可建立BTree的形式,下文将为你描述。 首先假设我们将需要索引的…

NSMakeRange基础函数应用
NSRange NSMakeRange (NSUInteger loc,NSUInteger len );这是官方得接口描述。loc 为location缩写,len 表示长度。作用:在loc指定得位置开始往后获取len长度个得元素。示例程序使用这个函数来为指定数组得某个位置向后指定长度得元素集合。如NSRange NSM…

上传图片,要求图片200100象素,大小小于2M
作者:网际浪子专栏(曾用名littlehb) http://blog.csdn.net/littlehb/上传图片,要求图片200100象素,大小小于2M,如果图片不符合要求,不能上传,否则上传图片,上传以后对图…
炸裂!这些大厂跪求的人才太牛了!
今年所有的互联网公司都在ALL in AI,百度、腾讯、阿里巴巴、京东等互联网巨头都在四处挖掘AI人才。AI的岗位需求很多,几乎每天都有数百个JD放出。而亿欧智库发布的《2020全球人工智能人才培养研究报告》提到,近4年AI人才的需求量以每年74%的速…

新春祝福必杀计之发送短信攻略
新的一年就要到了,陆陆续续的收到很多同事们的短信祝福,哎,不能欠人家的人情债啊,但是我实在是个懒人啊,这个祝福短信还是要回复的啊,我的手机里有超过百位联系人,全是和工作有关的,…
centos6.5环境DNS-本地DNS主从服务器bind的搭建
centos6.5环境DNS-本地DNS主从服务器bind的搭建在上一篇博客中我已经搭建好了一个本地DNS服务器,能够实现正向反向解析,那么我们只需要加入一台从DNS服务器即可完成,我们来开始配置主从服务器:一.主DNS服务器上面的额外…
再见,Python!
结合我最近这些年的 Python 学习、开发经验,发现近90%的程序员在学 Python 时都会遇到下面这3个问题:1.想学Python,但不知从何学起,应用方向太多了也不知道该选择什么方向...2.基础入门看似简单,但是进阶实战就举步维艰…

如何更新父窗体
Response.Write("<script languagejavascript>window.opener.locationwindow.opener.location.href;</script>") 第一种方案是:file a.htm function OpenDialog(url,param){return window.open(url,param, "DialogWidth:450px;DialogH…

文件体积单位的换算
单位转换(参考): 1bit(这个比特表示一个二进制数字) 1Byte(这个单词音译过来也叫“比特”但是表示一个十六进制的数字) 1B1Byte8bit 1 kB 1024 B (kB - kilobyte) 千 1 MB 1024 kB (MB - mega…

防止盗链下载问题
经常在网络上四处载东西,有时碰到直接拷贝一个类似 http://193.100.100.56/TestWebSolution/WebApplication1/test.rar地址准备下载test.rar文件时,却被告知没有登录或者直接跳转到其他页面的情况,然后等登录后直接下载该文件。要实现上面情况…
中国人工智能市场破 50 亿!你还不了解 AI 云服务吗?
作者 | 马超责编 | 伍杏玲出品 | CSDN(ID:CSDNnews)从AI的发展趋势来看,最新的人工智能模型对于算力的要求往往较高,如谷歌的T5,其整个模型的参数数量达到了惊人的 110 亿,谷歌科学家更在T5的论…

Java中数据存储方式
2019独角兽企业重金招聘Python工程师标准>>> 1. 寄存器(register)。这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你…

Oracle与JCP执行委员会分享了他们的Java EE策略
Anil Gaur是Oracle集团负责Java EE和WebLogic Server的副总裁。他受邀在上一次的JCP执行委员会会议上发表了演讲,透露了有关Java EE未来发展的一些信息。他所传达的信息和Oracle之前的说法一致:企业编程正在发生变化,Oracle希望适应这种变化。…
5G:新基建的压舱石,如何为新基建按下“加速键”?
受访嘉宾 | 广和通 CEO 应凌鹏记者 | 邓晓娟2019 年,伴随着 5G 的浪潮,5G、AI、AIoT等新兴技术得到了全面的爆发,过去人们认为遥不可及的「高科技」,也逐渐走下“神坛”来进入大家的生活。5G 时代的万物互联,在本次疫情…

no argument specified with option /LIBPATH:错误的解决
作者:朱金灿来源:http://blog.csdn.net/clever101下午使用VS2005编译工程碰到一个链接错误:no argument specified with option "/LIBPATH:"。检查了链接库的文件路径,发现某个路径是非法的(即不存在该文件路…

Animation 模拟纸盒的爆破
用简单的Animation动作模拟爆破的瞬间,仔细的调整各种参数效果会更像 原理:用定义好的纸张从onTouch中心点向四面八方散开,散开过程中,使用不用的速度、大小、方向、旋转角度、透明度(这里纸张可以加上火焰,…
代码恒久远,GitHub 永流传
作者 | 唐小引题图 | GitHub来源 | CSDN(ID:CSDNnews)这两天,在 GitHub 上积极贡献代码的许多开发者都收到了「Arctic Code Vault Contributor」的荣誉勋章的通知,并非常兴奋地晒起了朋友圈。因为这标志着自己在 GitHu…

不允许后退的方法
由于项目的需要不允许系统在提交之后,按IE的后退按钮进行再次提交。试试了一下在 .NET中通过如下语句 Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);就可以使页面的缓存失效,每次都需要获取新页面。 <script>history.for…

3个题目熟悉类和对象基础
1、按要求编写Java应用程序: (1)编写西游记人物类(XiYouJiRenWu)其中属性有:身高(height),名字(name),武器(weapon&#x…

按下回车表示确定提交
<body οnkeydοwn"if (event.keyCode13) {document.all.button2.click()}"> 下面的五种方法都可以帮你解决这种问题1.<script languagejavascript>function document.onkeydown(){ if (event.keyCode 13) { if (event.srcElement docum…
A股暴跌,户均亏2万!刚写好的辞职信又撕了……
仅用1天,A股市值单日蒸发达到3.5万亿,人均亏了超2万!“芯片龙头”企业中芯国际正式登陆科创板,使得半导体板块整体跌幅较小。中芯国际上市首日涨幅超200%,收报82.92元,总市值达6137.57亿元,成为…

DataList在无数据记录时显示类似GridView空模板(EmptyDataTemplate)
在FooterTemplate加个Label并根据repeater.Items.Count判断是否有记录。HTML代码: <FooterTemplate> <asp:Label ID"lblEmpty" Text"No data recprd exist !" runat"server" Visible<%#bool.Parse((DataList1.I…

QButtonGroup 的使用
1、3以后尽量手写,因为没有现在的控件了 2、 1 // lyy : 2016/8/26 12:17:41 说明:存放radioButton2 QButtonGroup *buttonGroup;3 // lyy : 2016/8/26 11:11:55 说明:radioButton4 buttonGroup new QButtonGroup();5 buttonGroup->addButt…
机器学习算法易受攻击?阿里“安全基建”这样应对AI的不安全
出品 | AI科技大本营(ID:rgznai100)数字基建的浪潮之下,诸多行业领域都在加速融合5G、大数据中心、AI等新技术,向数字化转型。近日,多位全国政协委员、院士和安全行业专家提出,应尽快出台安全基建国家标准&…