使用 Node.js 做前后端分离的开发模式带来了一些性能及开发流程上的优势(见《前后端分离的思考与实践 一》), 但同时也面临不少挑战。在淘宝复杂的业务及技术架构下,后端必须依赖Java搭建基础架构,同时提供相关业务接口供前端使用。Node.js 在整个环境中最重要的工作之一就是代理这些业务接口,以方便前端(Node.js 端和浏览器端)整合数据做页面渲染。如何做好代理工作,使得前后端开发分离之后,仍然可以在流程上无缝衔接,是我们需要考虑的问题。本文将就该问题做相关探讨,并提出解决方案。
由于后端提供的接口方式可能多种多样,同时开发人员在编写 Node.js 端代码访问这些接口的方式也有可能多种多样。如果我们在接口访问方式及使用上不做统一架构处理,则会带来以下一些问题:
于是我们希望有这样一个框架,通过该框架提供的机制去描述工程项目中依赖的所有外部接口,对他们进行统一管理,同时提供灵活的接口建模及调用方式,并且提供便捷的线上环境和生产环境切换方法,使前后端开发无缝结合。ModelProxy
就是满足这样要求的轻量级框架,它是 Midway Framework 核心构件之一,也可以单独使用。使用 ModelProxy
可以带来如下优点:
在上图中,开发者首先需要将工程项目中所有依赖的后端接口描述,按照指定的 JSON 格式,写入 interface.json
配置文件。必要时,需要对每个接口编写一个规则文件,也即图中 interface rules 部分。该规则文件用于在开发阶段mock数据或者在联调阶段使用 River 工具集去验证接口。规则文件的内容取决于采用哪一种 mock 引擎(比如 mockjs, river-mock 等等)。配置完成之后,即可在代码中按照自己的需求创建自己的业务 Model。
下面是一个简单的例子:
【例一】
{
"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"
}
}]
}
// 引入模块
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:
ModelProxy.create('Search.getItem');
ModelProxy.create({
getName: 'Session.getUserName',
getMyCarts: 'Cart.getCarts'
});
.
号后面的单词作为方法名ModelProxy.create(['Cart.getItem', 'Search.getItem', 'Search.suggest', 'Session.User.getName']);
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.js 端可以使用,也可以在浏览器端使用。只需要在页面中引入官方包提供的 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 以一种配置化的轻量级框架存在,提供友好的接口 model 组装及使用方式,同时很好的解决前后端开发模式分离中的接口使用规范问题。在整个项目开发过程中,接口始终只需要定义描述一次,前端开发人员即可引用,同时使用 River 工具自动生成文档,形成与后端开发人员的契约,并做相关自动化测试,极大地优化了整个软件工程开发过程。
【注】River 是阿里集团研发的前后端统一接口规范及相关工具集合的统称
← 前后端分离的思考与实践(四) 前后端分离的思考与实践(二) →题图:https://unsplash.com/photos/k8w64_keVSg By @Alfred Quartey