基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架


Posted in NodeJs onSeptember 26, 2014

前言

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

基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架

由于后端提供的接口方式可能多种多样,同时开发人员在编写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工作原理图及相关开发过程图览

基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架

在上图中,开发者首先需要将工程项目中所有依赖的后端接口描述,按照指定的json格式,写入interface.json配置文件。必要时,需要对每个接口编写一个规则文件,也即图中interface rules部分。该规则文件用于在开发阶段mock数据或者在联调阶段使用River工具集去验证接口。规则文件的内容取决于采用哪一种mock引擎(比如 mockjs, river-mock 等等)。配置完成之后,即可在代码中按照自己的需求创建自己的业务model。

下面是一个简单的例子:

【例一】

第一步 在工程目录中创建接口配置文件interface.json, 并在其中添加主搜接口json定义

{

    "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

// 引入模块

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最后'.'号后面的单词作为方法名

ModelProxy.create( 'Search.getItem' );

使用键值JSON对象>自定义方法名: 接口ID

ModelProxy.create( {

    getName: 'Session.getUserName',

    getMyCarts: 'Cart.getCarts'

} );

使用数组形式>取最后 . 号后面的单词作为方法名
下例中生成的方法调用名依次为: Cart_getItem, getItem, suggest, getName

ModelProxy.create( [ 'Cart.getItem', 'Search.getItem', 'Search.suggest', 'Session.User.getName' ] );

前缀形式>所有满足前缀的接口ID会被引入对象,并取其后半部分作为方法名

ModelProxy.create( 'Search.*' );

同时,使用这些Model,你可以很轻易地实现合并请求或者依赖请求,并做相关模板渲染

【例二】 合并请求

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 );

    } );

【例三】 依赖请求

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

<!-- 引入modelproxy模块,该模块本身是由KISSY封装的标准模块-->

<script src="modelproxy-client.js" ></script>

<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 是阿里集团研发的前后端统一接口规范及相关工具集合的统称

NodeJs 相关文章推荐
nodejs 后缀名判断限制代码
Mar 31 NodeJs
windows系统下简单nodejs安装及环境配置
Jan 08 NodeJs
nodejs读取memcache示例分享
Jan 02 NodeJs
基于NodeJS的前后端分离的思考与实践(二)模版探索
Sep 26 NodeJs
Nodejs关于gzip/deflate压缩详解
Mar 04 NodeJs
Nodejs如何搭建Web服务器
Mar 28 NodeJs
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
Jan 12 NodeJs
NodeJS仿WebApi路由示例
Feb 28 NodeJs
nodejs获取微信小程序带参数二维码实现代码
Apr 12 NodeJs
NodeJS实现自定义流的方法
Aug 01 NodeJs
NodeJS使用Range请求实现下载功能的方法示例
Oct 12 NodeJs
5分钟教你用nodeJS手写一个mock数据服务器的方法
Sep 10 NodeJs
基于NodeJS的前后端分离的思考与实践(二)模版探索
Sep 26 #NodeJs
基于NodeJS的前后端分离的思考与实践(一)全栈式开发
Sep 26 #NodeJs
Nodejs Post请求报socket hang up错误的解决办法
Sep 25 #NodeJs
Nodejs实现的一个简单udp广播服务器、客户端
Sep 25 #NodeJs
Nodejs异步回调的优雅处理方法
Sep 25 #NodeJs
nodejs命令行参数处理模块commander使用实例
Sep 17 #NodeJs
nodejs npm package.json中文文档
Sep 04 #NodeJs
You might like
php开发环境配置记录
2011/01/14 PHP
php调用方法mssql_fetch_row、mssql_fetch_array、mssql_fetch_assoc和mssql_fetch_objcect读取数据的区别
2012/08/08 PHP
php中addslashes函数与sql防注入
2014/11/17 PHP
PHP多文件上传类实例
2015/03/07 PHP
PHP引用返回用法示例
2016/05/28 PHP
详解no input file specified 三种解决方法
2019/11/29 PHP
jQuery Flash/MP3/Video多媒体插件
2010/01/18 Javascript
jquery 检测元素是否存在的实例代码
2013/11/19 Javascript
表单序列化与jq中的serialize使用示例
2014/02/21 Javascript
jquery使用$(element).is()来判断获取的tagName
2014/08/24 Javascript
简单谈谈jQuery(function(){})与(function(){})(jQuery)
2014/12/19 Javascript
bootstrap使用validate实现简单校验功能
2016/12/02 Javascript
浅谈js中function的参数默认值
2017/02/20 Javascript
Linux Centos7.2下安装nodejs&amp;npm配置全局路径的教程
2018/05/15 NodeJs
jQuery对底部导航进行跳转并高亮显示的实例代码
2019/04/23 jQuery
vue在线动态切换主题色方案
2020/03/26 Javascript
[01:42]DOTA2 – 虚无之灵
2019/08/25 DOTA
python实现封装得到virustotal扫描结果
2014/10/05 Python
Python线程详解
2015/06/24 Python
使用Python求解最大公约数的实现方法
2015/08/20 Python
pytorch中的上采样以及各种反操作,求逆操作详解
2020/01/03 Python
python实现二分类和多分类的ROC曲线教程
2020/06/15 Python
PyCharm2019.3永久激活破解详细图文教程,亲测可用(不定期更新)
2020/10/29 Python
HTML5拖放API实现拖放排序的实例代码
2017/05/11 HTML / CSS
试解释COMMIT操作和ROLLBACK操作的语义
2014/07/25 面试题
司机岗位职责
2013/11/15 职场文书
项目副经理岗位职责
2013/12/30 职场文书
农民入党思想汇报
2014/01/03 职场文书
校园奶茶店创业计划书
2014/01/23 职场文书
中秋手机店促销方案
2014/06/16 职场文书
支部书记四风问题自我剖析材料
2014/09/29 职场文书
工作态度不端正检讨书
2014/10/04 职场文书
群众路线党员个人整改措施
2014/10/27 职场文书
2016新教师培训心得体会范文
2016/01/08 职场文书
Nginx 路由转发和反向代理location配置实现
2021/11/11 Servers
使用Redis实现点赞取消点赞的详细代码
2022/03/20 Redis