前言使用Node作为前后端分离的开发模型,在性能和开发过程上带来一些优势(见《前后端分离的思考与实践 一》),但同时也有有很多挑战。在淘宝复杂的业务和技术架构下,后台必须依赖Java搭建基础设施,同时提供相关业务接口给前台使用。Node在整个环境中最重要的工作之一就是代理这些业务接口,方便前端(Node和浏览器)集成数据进行页面渲染。如何做好代理工作,让前后端开发分离后,流程仍然可以无缝对接,这是我们需要考虑的问题。本文将讨论这个问题并提出解决方案。由于后端提供的接口方法可能多种多样,同时,开发者在编写Node端代码时可能会以多种方式访问??这些接口。如果我们在接口访问方式和使用上不实现统一的架构,会带来以下问题:1.每个开发者都使用自己的代码风格来编写接口访问代码,导致项目目录和编码风格混乱,而且维护起来比较麻烦。2.每个开发者编写自己的模拟数据方法。开发完成后,需要手动修改代码,去除mock。3.每个开发者可能会维护一些配置文件,以便在接口的不同环境(日常、预发布、在线)之间切换。4、数据接口调用方式不能轻易被各种业务模型复用。5、数据接口的描述协议散落在代码的各个角落,可能与后台人员约定的接口文档不一致。6.整个项目单独开发后,接口联调或测试回归的成本仍然很高,需要每个接口提供者和用户都参与进来。所以我们希望有这样一个框架,通过框架提供的机制来描述项目所依赖的所有外部接口,统一管理,提供灵活的接口建模和调用方式,提供方便的线上环境和生产环境切换方式,实现前后端开发无缝融合。ModelProxy就是满足这样需求的轻量级框架。它是MidwayFramework的核心组件之一,也可以单独使用。使用ModelProxy可以带来以下好处:1、不同的开发者有统一的接口访问代码编写方式,含义明确,降低了维护难度。2、框架内部采用工厂+单例模式,实现一次配置接口的多次复用。并且开发者可以随意定制组装自己的业务模型(依赖注入)。3.线上、日常、预发环境切换非常方便。4、内置river-mock、mockjs等mock引擎,提供mock数据非常方便。5、使用接口配置文件统一管理接口的依赖描述,避免分散在各种代码中。6、支持浏览器端Model的共享,浏览器端可以使用它来进行前端数据渲染。整个代理过程对浏览器是透明的。7、接口配置文件本身是一个结构化的描述文档,可以使用rivertoolcollection自动生成该文档。也可以用于相关的自动化接口测试,使整个开发过程形成一个闭环。ModelProxy的工作原理图和相关的开发流程图如上图所示。开发者首先需要将项目依赖的所有后端接口的描述以指定的json格式写入interface.json配置文件中。如果需要,还需要为每个接口编写一个规则文件,即图中的接口规则部分。该规则文件用于开发阶段mock数据或联调阶段使用River工具集验证接口。规则文件的内容取决于使用的是哪个mock引擎(比如mockjs、river-mock等)。配置完成后,您可以根据需要在代码中创建自己的业务模型。下面是一个简单的示例:【示例1】第一步在项目目录下创建接口配置文件interface.json,添加主搜索接口json定义{"title":"pad淘宝项目数据接口集合定义","version":"1.0.0","engine":"mockjs","rulebase":"./interfaceRules/","status":"online","interfaces":[{"name":"mainSearchinterface","id":"Search.getItems","urls":{"online":"http://s.m.taobao.com/client/search.do"}}]}第二步创建在代码中并使用模型//导入模块varModelProxy=require('modelproxy');//全局初始化导入接口配置文件(注:只有一个初始化工作)ModelProxy.init('./interface.json');//create更多的模型创建方式请参考下文varsearchModel=newModelProxy({searchItems:'Search.getItems'//自定义方法名:配置文件中定义的接口ID});//使用模型,注意:调用方法所需的参数是实际接口需要的参数。searchModel.searchItems({q:'iphone6'})//!注意必须调用done方法指定回调函数来获取上面异步调用searchItems得到的数据!.done(function(data){console.log(data);}).error(function(err){console.log(err);});#p#ModelProxy功能丰富,支持各种形式的profile来创建需要创建的业务模型:使用接口ID创建>生成的对象会取ID***'后的词。作为方法名称ModelProxy.create('Search.getItem');使用键值JSON对象>自定义方法名称:interfaceIDModelProxy.create({getName:'Session.getUserName',getMyCarts:'Cart.getCarts'});使用数组形式>取***后面的词。作为方法名称。以下示例中生成的方法调用名称为:Cart_getItem、getItem、suggest、getNameModelProxy.create(['Cart.getItem','Search.getItem','Search.suggest','Session.User.getName']);前缀形式>所有符合前缀的接口ID都会被引入到对象中,后半部分作为方法名ModelProxy.create('Search.*');同时,利用这些Models,可以方便的实现mergerequests或者dependentrequests,做相关的模板渲染【例2】mergerequestvarmodel=newModelProxy('Search.*');//合并请求(除了done,下面调用的模型方法都是在配置接口id的时候指定的)model.suggest({q:'female'}).list({keyword:'iphone6'}).getNav({key:'fashionclothing'}).done(function(data1,data2,data3){//参数顺序与方法调用顺序一致console.log(data1,data2,data3);});【例3】赖请求varmodel=newModelProxy({getUser:'Session.getUser',getMyOrderList:'Order.getOrder'});//先获取用户id,再获取订单列表model.getUser({sid:'fdkaldjfgsakls0322yf8'}).done(function(data){vauid=data.uid;//根据id号进行二次数据请求第一次获取this.getMyOrderList({id:uid}).done(function(data){console.log(data);});});另外,ModelProxy不仅可以在Node端使用,也可以在浏览器端使用,只需要在页面中导入官方包提供的modelproxy-client.js即可。【示例4】在浏览器端使用ModelProxy同时,ModelProxy可以与Midway的另一核心组件Midway-XTPL配合使用,实现数据和Templates及相关渲染过程在浏览器和服务器端完全共享。关于ModelProxy的详细教程和文档,请访问https://github.com/purejs/modelproxy。总结ModelProxy作为一个可配置的轻量级框架而存在,提供了友好的接口模型组装和使用,很好地解决了前后端分离开发模式下接口使用规范化的问题。在整个项目开发过程中,接口始终只需要定义和描述一次,前端开发人员就可以参考。同时利用River工具自动生成文档,与后端开发者签订合同,进行相关的自动化测试,极大的优化了整个项目。软件工程开发过程。【注】River是阿里集团制定的前后端统一接口规范及相关工具集的统称。原文链接:http://ued.taobao.org/blog/2014/04/modelproxy/
