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

前言

使用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定义

代码如下:

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

(0)

相关推荐

  • NodeJS Express框架中处理404页面一个方式

    在用 Express 的时候,路由是我最困惑的事之一.知道用 app.get('*') 可以处理所有页面,但这样除了自定义的其他路由外,静态文件是被忽略的.最近在写一个小工具的时候,找到了一个解决方案: 复制代码 代码如下: var express = require('express'),    router = require('./routes'); var app = module.exports = express.createServer(); // Configurationapp

  • nodeJs爬虫获取数据简单实现代码

    本文实例为大家分享了nodeJs爬虫获取数据代码,供大家参考,具体内容如下 var http=require('http'); var cheerio=require('cheerio');//页面获取到的数据模块 var url='http://www.jcpeixun.com/lesson/1512/'; function filterData(html){ /*所要获取到的目标数组 var courseData=[{ chapterTitle:"", videosData:{ v

  • Nodejs爬虫进阶教程之异步并发控制

    之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一个问题的请求链接,取得的页面是不完整的.还有就是我们通过发送链接下载图片的时候,是一张一张来下的,如果图片数量太多的话,真的是下到你睡完觉它还在下,而且我们用nodejs写的爬虫,却竟然没有用到nodejs最牛逼的异步并发的特性,太浪费了啊. 思路 这次的的爬虫是上次那个的升级版,不过呢,上次那个虽

  • NodeJS框架Express的模板视图机制分析

    模板引擎 Express支持许多模板引擎,常用的有: haml 的实现Haml haml.js 接替者,同时也是Express的默认模板引擎Jade 嵌入JavaScript模板EJS 基于CoffeeScript的模板引擎CoffeeKup 的NodeJS版本jQuery模板引擎 视图渲染(view randering) 视图的文件名默认需遵循"<name>.<engine>"的形式,这里<engine>是要被加载的模块的名字.比如视图layout

  • Nodejs全栈框架StrongLoop推荐

    StrongLoop是一个基于Nodejs的强大框架,几乎包含了移动开发全栈所需要的所有功能.2013年成立,很少的员工,一个技术驱动,执行力强大的团队.也是在13年我开始接触StrongLoop,当时是为了做nodejs方面的技术选型,看了许多框架,LoopBack是我觉得最酷的一个.我还记得当时是觉得LoopBack的文档太差(主要是跟在线的版本不一样),不知道能活多久所以才放弃了它.时隔一年回来看到这个绿油油的框架,这一年可真是突飞猛进呢. 全栈框架StrongLoop StrongLoo

  • 简单好用的nodejs 爬虫框架分享

    这个就是一篇介绍爬虫框架的文章,开头就不说什么剧情了.什么最近一个项目了,什么分享新知了,剧情是挺好,但介绍的很初级,根本就没有办法应用,不支持队列的爬虫,都是耍流氓. 所以我就先来举一个例子,看一下这个爬虫框架是多么简单并可用. 第一步:安装 Crawl-pet nodejs 就不用多介绍吧,用 npm 安装 crawl-pet $ npm install crawl-pet -g --production 运行,程序会引导你完成配置,首次运行,会在项目目录下生成 info.json 文件 $

  • Nodejs Express4.x开发框架随手笔记

    Express: ?web application framework for?Node.js?Express 是一个简洁.灵活的 node.js Web 应用开发框架, 它提供一系列强大的特性,帮助你创建各种 Web 和移动设备应用. 目录 此文重点介绍Express4.x(具体是4.10.4)的开发框架,其中还会涉及到Mongoose,Ejs,Bootstrap等相关内容. 建立工程 目录结构 Express4.x配置文件 Ejs模板使用 Bootstrap界面框架 路由功能 Session

  • NodeJS制作爬虫全过程

    今天来学习alsotang的爬虫教程,跟着把CNode简单地爬一遍. 建立项目craelr-demo 我们首先建立一个Express项目,然后将app.js的文件内容全部删除,因为我们暂时不需要在Web端展示内容.当然我们也可以在空文件夹下直接 npm install express来使用我们需要的Express功能. 目标网站分析 如图,这是CNode首页一部分div标签,我们就是通过这一系列的id.class来定位我们需要的信息. 使用superagent获取源数据 superagent就是

  • 14款NodeJS Web框架推荐

    在几年的时间里,Node.js逐渐发展成一个成熟的开发平台,吸引了许多开发者.有许多大型高流量网站都采用Node.js进行开发,像PayPal,此外,开发人员还可以使用它来开发一些快速移动Web框架. 下面就介绍14款基于Node.js的Web应用框架,大家不妨过来看看有没有适合你的那一款. 1.Primus Primus,是Transformer的创造者,并且也被称为通用包装器实时框架.Primus里包含了大量的用于Node.js的实时框架,并且它们都拥有各种不同的实时功能.此外,Primus

  • NodeJS制作爬虫全过程(续)

    书接上回,我们需要修改程序以达到连续抓取40个页面的内容.也就是说我们需要输出每篇文章的标题.链接.第一条评论.评论用户和论坛积分. 如图所示,$('.reply_author').eq(0).text().trim();得到的值即为正确的第一条评论的用户. {<1>} 在eventproxy获取评论及用户名内容后,我们需要通过用户名跳到用户界面继续抓取该用户积分 复制代码 代码如下: var $ = cheerio.load(topicHtml); //此URL为下一步抓取目标URL var

  • nodejs爬虫抓取数据之编码问题

    cheerio DOM化并解析的时候 1.假如使用了 .text()方法,则一般不会有html实体编码的问题出现 2.如果使用了 .html()方法,则很多情况下(多数是非英文的时候)都会出现,这时,可能就需要转义一番了 类似这些 因为需要作数据存储,所有需要转换 复制代码 代码如下: Халк крушит. Новый способ исполнен 大多数都是&#(x)?\w+的格式 所以就用正则转换一番 var body = ....//这里就是请求后获得的返回数据,或者那些 .html

  • nodejs爬虫抓取数据乱码问题总结

    一.非UTF-8页面处理. 1.背景 windows-1251编码 比如俄语网站:https://vk.com/cciinniikk 可耻地发现是这种编码 所有这里主要说的是 Windows-1251(cp1251)编码与utf-8编码的问题,其他的如 gbk就先不考虑在内了~ 2.解决方案 1. 使用js原生编码转换 但是我现在还没找到办法哈.. 如果是utf-8转window-1251还可以http://stackoverflow.com/questions/2696481/encoding

随机推荐