老司機都有體會, 開發自己沒有多難, 最糾結實際上是最初的技術和框架選型, 本沒有絕對的好壞之分, 可一旦選擇了不適合於本身業務場景的框架, 未來木已成舟後開發和維護成本都很高, 等發現不合適的時候更換的成本更是使人膽顫, 數據觀最先的接入層是採用ThinkPHP開發, 後來基於種種權衡後決定用node.js重製, web開發框架選型就成爲首要必須慎重解決的問題, Express固然是頭號映入視野的名字, 本着全面考察重點擇優的原則又花很多時間簡單研究對比幾個主流的node.js web開發框架, 真的有亂花漸欲迷人眼的感受, 把當初列入備選的幾個框架簡單評論一下:
- Express
這個就無需再介紹了吧, 幾乎已經成爲閉眼推薦的首位, 有點在於簡單靈活, 缺陷也在於過於簡單, 至關於每一個功能都須要本身選擇不一樣組件搭建, 雖然有各自腳手架幫助, 但對於開發大一點的系統仍是缺少必要的代碼框架, 光搭建整合基礎框架就會花很多時間, 對於新上手node.js不久的筆者而言,自由靈活也意味着容易犯錯, 最好有相似Djongo/ThinkPHP那樣out-of-box即開即用的開發框架快速上手而不是坐而論道.
優勢: 插件衆多, 簡單, 自由, 豐儉由人, 適合於簡單業務邏輯模型
缺點: 缺乏規範性, 須要本身選擇搭配的組件太多, 不太適合應用複雜的業務.
- Koa
Express基於ES6的升級版, async/await解決ES5 callback hell的痼疾, 可是選擇框架不只僅是框架自己, 同時還要看插件擴展的豐富和成熟度, 由於沒有用過擔憂後面遇坑填不平而放棄, 作研究能夠大膽, 作產品必須謹慎.
優勢: ES6語法, 邏輯易懂
缺點: 剛開始應用不久, 擔憂有擴展不足和不兼容問題
- Meteor
一個徹底統一先後臺開發的一站式框架, 從後臺數據庫到前端view所有包含在內, 特別適合於重度依賴websocket的SPA(單頁面應用)開發, 國外流行的Asana就是徹底採用Meteor框架開發.
優勢:一站式解決方案, 先後臺一體開發, 強大的websocket + mongoDB支持
缺點:自由度不夠, 和傳統的Web框架概念差別較大.
- socketstream
若是說RESTful是Ajax的概念基礎, socketstream實際上更接近於早期RPC的思路, 將函數調用(function name + parameters)建構在websocket協議層之上, 例以下面這段代碼其實就是調用遠程函數計算.
//-- server side in /server/rpc/app.js
exports.actions =
function
(
req, res, ss
){
// return list of actions which can be called publicly
return {
square:
function
(
number
){
res(number * number);
}
}
}
//-- client request
ss.rpc(
'app.square',
25)
優勢: 徹底web socket, 函數+參數==>返回值概念簡單
缺點:和主流的RESTful概念偏離較大, 不多看到實際應用案例
- Sails.js
這是本文的主角, 至關於針對典型應用框架所需組件在Express基礎上的集成封裝, 把平常開發經常使用的功能都給你集成好了, 開箱即用, 徹底兼容Express的middleware, 若是瞭解ThinkPHP就更容易上手了, RoR / Convention over Configuration的概念能夠當即進入實際業務開發, 反正作什麼事情應該怎麼作人家都給你規定好了, 能夠從實驗代碼逐步迭代到中大項目的生產代碼.
除了傳統的HTTP RESTful外還同時支持websocket - 同一個請求協議既能夠經過Ajax發送, 也能夠經過websocket發送, 這一點讓人讚揚.
優勢: 開箱即用的全功能Express加強框架, 內置支持websocket
缺點:(聽說)ORM性能很差
通過半個月的技術調研和評估後最終決定將原來基於ThinkPHP開發的接入控制層所有移植到Sails.js框架上, 雖然不免遇到技術問題須要解決, 但總體而言進展順利, API訪問延遲從100ms下降到10幾ms,主要是今後有了一個Service Runtime解決PHP無狀態而強依賴Redis的問題.
Sails.js 框架組成介紹
既然是一個全功能的MVC框架, 那麼開發一個應用所需的經常使用組件天然包含在內除非你有特殊要求, 這一點很是相似ThinkPHP, 從目錄分佈上介紹一個概貌先:
-
api/
Model / Controller都在這個目錄下, 全部代碼根據不一樣職責都被定義好位置, 這一點很是有利於大型項目開發的代碼規劃.
-
api/controllers
這裏面寫的每個controller + function 都會被自動映射到路由上, 充分體現convention over configuration的特色, 與ThinkPHP一模一樣, 來一個最基礎的hello world吧 -
api/controllers/TestController.hello() 方法被自動映射到
/test/hello 而無需任何配置.
// api/controllers/TestController.js
module.exports = {
hello:
function
(
req, res
) {
return res.ok(
"Hello World");
},
controller下能夠隨意進行目錄分層, 每增長一級目錄就至關增長一級路由.
api/controllers/user/TestController.hello() ==> /user/test/hello
- api/services
項目大了程序就必須進行職能分層, 業務相關都做爲獨立的service一層放在這個目錄下, controller僅負責request / response及差錯處理, 既簡化controller的代碼邏輯, 又可讓service代碼被不一樣模塊複用。做用至關於一套全局函數庫。
常見錯誤: service代碼不要直接經過res作應答, 那原本是屬於controller的職責.
- api/policies
做爲先後臺分離必然要提供Ajax API給前端, 那麼訪問鑑權和安全控制必不可少, Sails.js在Express middleware基礎上封裝爲policy實現訪問控制, 若是說上面那個 /test/hello 僅容許登陸用戶才能訪問, 那麼加一個驗證session的policy就是.
langset是檢測多語言設置的policy, sessionAuth僅容許帶有登陸session的API請求經過, 多個policy能夠組成一個檢查鏈, 只有全經過了才能達到最終的controller, 中間任何一個policy均可以直接response拒絕服務.
config/policies的鑑權配置
module.
exports.policies = {
'*':
true,
TestController: {
'*' :
'langset',
'hello' : [
'langset',
'sessionAuth'],
},
api/policies/sessionAuth.js實現
若是是頁面請求則redirect, 若是是ajax則應答403 forbidden.
這裏還借用了passport的req.isAuthenticated(), 根據業務須要能夠結合多種檢查方式, 把每一個獨立檢查點都做爲一個policy, 而後配置爲一個policy chain, 功能獨立又能夠靈活搭配, 相似應用登陸檢查中的異地登陸, 長時間未登陸, 連續密碼錯誤, 動態驗證碼校驗均可以用policy的思路去實現.
module.exports =
function
(
req, res, next
) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
if (req.isAuthenticated()) {
return next();
}
// User is not allowed
// (default res.forbidden() behavior can be overridden in `config/403.js`)
if (req.wantsJSON) {
return res.forbidden(
'You are not permitted to perform this action.');
}
return res.redirect(
"/login");
};
- api/responses
經常使用的response頁面都在這裏了, 固然能夠增長新的, 調用就是res.{response_name}(message)
* badRequest.js - 400
* created.js - 201
* forbidden.js - 403
* notFound.js - 404
* ok.js - 200
* serverError.js - 500
Oops, 404了。
- api/models
Sails.js的ORM實現, 這個目錄下的class都會映射爲一個持久化的object及attributes, 採用Waterline hook機制實現, 能夠配置爲MySQL/MongoDB/Oracle/PostgreSQL多種常見數據庫.
考慮到node.js更多做爲中間轉接層角色出現,真正ORM與核心業務邏輯仍是交由Java等後臺實現爲妥,因此我的並不建議過多采用Sails.js自身的ORM,聊勝於無吧。
這是原生ejs模板實現的403:
<
h2>
<
%
if (typeof data !==
'undefined') { %>
<
%= data %>
<
% }
else { %>
You don't have permission to see the page you're trying to reach.
<
% } %>
</
h2>
這是handlebars實現的403
<
div
class=
"content">
{{#
if data }}
{{data}}
{{
else
}}
您沒有權限查看您要訪問的網頁。
{{/
if}}
</
div>
Sails.js框架經常使用搭配的七種武器
框架自身所能提供的功能畢竟是有限的,擴展庫纔是一個框架豐滿與否的關鍵,此處結合筆者的實踐推薦幾個可用的框架擴展。
1. 長生劍-async.js + lodash.js
這是兩個被內置的核心js庫,前者用於流程控制,後者用於數據結構處理,即便不用sails.js也應該深刻掌握。
2. 碧玉刀-handlebars
放棄ejs吧,handlebars更優雅簡潔,並且具備更強更容易的處理邏輯擴展能力。所有祕密就在於註冊一個本身的函數。
這是一個實現編碼轉義的helper函數
Handlebars.registerHelper(
'unescape',
function
(
v
) {
return
unescape(v);
});
3. 離別鉤-winston
寫後臺不充分考慮log記錄簡直等於不帶手機去旅行,sails-hook-winston能夠無縫將sails.log轉接到任意winston transport插件上而無需修改任何代碼。
console / winston-daily-rotate-file / http / LogstashRedis 是實際使用中的transport。
4. 霸王槍-passport
基於passport的第三方認證已經成爲JS的標準,幾百個知名strategy即插即用,幾行配置代碼就能夠實現微信/微博/釘釘/QQ第三方登陸認證,固然最好對OAuth2有簡單瞭解。
5. 多情環-socket.io
Sails.js缺省是自帶websocket支持的,並且其model的CRUD操做也自動支持pub/sub操做,一個Ajax請求不管是走HTTP協議仍是websocket協議在controller是同一個處理入口,header/session都是同樣的,這一特色真切讓人感到體貼,沒必要再針對不一樣功能採用不一樣協議而後生成不一樣代碼了。
CAUTION: 不能用於阿里雲防火牆後面的應用服務
實測下來socket會頻繁斷掉重連,並且每次都是不一樣的IP地址,懷疑兩點原由:
- 阿里防火牆專門針對HTTP無狀態短鏈接設計,不容許出現長期的socket鏈接。
- 爲了負載分擔和惡意攻擊防禦,不一樣請求入口IP地址會變化。
6. 孔雀翎-pm2
若是說Java的代碼穩定是靠語法和Exception捕獲來保證的,那麼node.js的邏輯反其道行之-
若是錯了就儘快退出執行,反正catch回調函數拋出的異常就是水中月,進程生命週期管理最佳工具就是pm2,一行代碼都不用寫便可支持cluster運行模式,即便偶爾出錯致使進程退出也會自動重啓,確保服務不間斷。
7. 拳頭-typescript
後臺應用服務最關鍵的要求是什麼?穩定,穩定,仍是TMD的穩定。
面對一個過於自由沒有類型檢查的javascript語言,防範編碼錯誤的最佳武器就是套上類型語言的盔甲,class / interface / extends / enum etc,少許代碼時這是累贅,面對大量代碼和複雜結構與邏輯時有語言的支撐纔是保命的祕訣。
後記
寫了這麼多,總結了本身在開發數據觀中間層時候的一些經驗但願對各位Web開發者有些許參考,其實更可悲的是現今Web開發並無能一統江山的框架(並且短時間還真看不到這個但願),隨着時間流逝和技術的飛速發展,恁多風流不免被雨打風吹去,枉回首,如落紅湮滅。