# Midway-ModelProxy — 輕量級的接口配置建模框架
## 前言
使用Node做前后端分離的開發模式帶來了一些性能及開發流程上的優勢(見?[《前后端分離的思考與實踐 一》](http://ued.taobao.org/blog/2014/04/full-stack-development-with-nodejs/)?), 但同時也面臨不少挑戰。在淘寶復雜的業務及技術架構下,后端必須依賴Java搭建基礎架構,同時提供相關業務接口供前端使用。Node在整個環境中最重要的工作之一就是代理這些業務接口,以方便前端(Node端和瀏覽器端)整合數據做頁面渲染。如何做好代理工作,使得前后端開發分離之后,仍然可以在流程上無縫銜接,是我們需要考慮的問題。本文將就該問題做相關探討,并提出解決方案。

由于后端提供的接口方式可能多種多樣,同時開發人員在編寫Node端代碼訪問這些接口的方式也有可能多種多樣。如果我們在接口訪問方式及使用上不做統一架構處理,則會帶來以下一些問題:
1. 每一個開發人員使用各自的代碼風格編寫接口訪問代碼,造成工程目錄及編碼風格混亂,維護相對困難。
2. 每一個開發人員編寫自己的mock數據方式,開發完畢之后,需要手工修改代碼移除mock。
3. 每一個開發人員為了實現接口的不同環境切換(日常,預發,線上),可能各自維護了一些配置文件。
4. 數據接口調用方式無法被各個業務model非常方便地復用。
5. 對于數據接口的描述約定散落在代碼的各個角落,有可能跟后端人員約定的接口文檔不一致。
6. 整個項目分離開發之后,對于接口的聯調或者測試回歸成本依然很高,需要涉及到每一個接口提供者和使用者。
于是我們希望有這樣一個框架,通過該框架提供的機制去描述工程項目中依賴的所有外部接口,對他們進行統一管理,同時提供靈活的接口建模及調用方式,并且提供便捷的線上環境和生產環境切換方法,使前后端開發無縫結合。ModelProxy就是滿足這樣要求的輕量級框架,它是Midway Framework 核心構件之一,也可以單獨使用。使用ModelProxy可以帶來如下優點:
1. 不同的開發者對于接口訪問代碼編寫方式統一,含義清晰,降低維護難度。
2. 框架內部采用工廠+單例模式,實現接口一次配置多次復用。并且開發者可以隨意定制組裝自己的業務Model(依賴注入)。
3. 可以非常方便地實現線上,日常,預發環境的切換。
4. 內置?[river-mock](http://gitlab.alibaba-inc.com/river/mock/tree/master)?和?[mockjs](http://mockjs.com/)?等mock引擎,提供mock數據非常方便。
5. 使用接口配置文件,對接口的依賴描述做統一的管理,避免散落在各個代碼之中。
6. 支持瀏覽器端共享Model,瀏覽器端可以使用它做前端數據渲染。整個代理過程對瀏覽器透明。
7. 接口配置文件本身是結構化的描述文檔,可以使用?[river](http://gitlab.alibaba-inc.com/river/spec/tree/master)?工具集合,自動生成文檔。也可使用它做相關自動化接口測試,使整個開發過程形成一個閉環。
## 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](http://search.npm.taobao.net/package/midway-xtpl)一起使用,實現數據和模板以及相關渲染過程在瀏覽器端和服務器端的全共享。關于ModelProxy的詳細教程及文檔請移步[https://github.com/purejs/modelproxy](https://github.com/purejs/modelproxy)
## 總結
ModelProxy以一種配置化的輕量級框架存在,提供友好的接口model組裝及使用方式,同時很好的解決前后端開發模式分離中的接口使用規范問題。在整個項目開發過程中,接口始終只需要定義描述一次,前端開發人員即可引用,同時使用River工具自動生成文檔,形成與后端開發人員的契約,并做相關自動化測試,極大地優化了整個軟件工程開發過程。
【注】River 是阿里集團研發的前后端統一接口規范及相關工具集合的統稱
- 開始
- 微信小程序
- 獲取用戶信息
- 記錄
- HTML
- HTML5
- 文檔根節點
- 你真的了解script標簽嗎?
- 文檔結構
- 已經落后的技術
- form表單
- html實體
- CSS
- css優先級 & 設計模式
- 如何編寫高效的 CSS 選擇符
- 筆記
- 小計
- flex布局
- 細節體驗
- Flex
- Grid
- tailwindcss
- JavaScript
- javascript物語
- js函數定義
- js中的數組對象
- js的json解析
- js中數組的操作
- js事件冒泡
- js中的判斷
- js語句聲明會提前
- cookie操作
- 關于javascript你要知道的
- 關于innerHTML的試驗
- js引擎與GUI引擎是互斥的
- 如何安全的修改對象
- 當渲染引擎遇上強迫癥
- 不要使用連相等
- 修改數組-對象
- 算法-函數
- 事件探析
- 事件循環
- js事件循環中的上下文和作用域的經典問題
- Promise
- 最佳實踐
- 頁面遮罩加載效果
- 網站靜態文件之思考
- 圖片加載問題
- 路由及轉場解決方案
- web app
- 寫一個頁面路由轉場的管理工具
- 談編程
- 技術/思想的斗爭
- 前端技術選型分析
- 我想放點html模板代碼
- 開發自適應網頁
- 后臺前端項目的開發
- 網站PC版和移動版的模板方案
- 前后端分離
- 淘寶前后端分離
- 前后端分離的思考與實踐(一)
- 前后端分離的思考與實踐(二)
- 前后端分離的思考與實踐(三)
- 前后端分離的思考與實踐(四)
- 前后端分離的思考與實踐(五)
- 前后端分離的思考與實踐(六)
- 動畫
- 開發小技巧
- Axios
- 屏幕適配
- 理論基礎
- 思考
- flexible.js原理
- 實驗
- rem的坑,為什么要設置成百分比,為什么又是62.5%
- 為什么以一個標準適配的,其它寬度也能同等適配
- 自適應、響應式、彈性布局、屏幕適配
- 適配:都用百分比?
- 番外篇
- 給你看看0.5px長什么樣?
- 用事實證明viewport scale縮放不會改變rem元素的大小
- 為什么PC端頁面縮放不會影響rem元素
- 究竟以哪個為設備獨立像素
- PC到移動端初試
- 深入理解px
- 響應式之柵格系統
- 深入理解px(二)
- 一篇搞定移動端適配
- flex版柵格布局
- 其他
- 瀏覽器加載初探
- 警惕你的開發工具
- JS模塊化
- webpack
- 打包原理
- 異步加載
- gulp
- 命名規范
- 接口開發
- sea.js學習
- require.js學習
- react學習
- react筆記
- vue學習
- vue3
- 工具、技巧
- 臨時筆記
- 怎么維護好開源項目
- 待辦
- 對前端MVV*C框架的思考
- jquery問題
- 臨時
- 好文
- 節流防抖