為什麼使用Sails?

weixin_34127717發表於2016-03-17

原文連結:BlueSun | 為什麼使用Sails?

前言

入手Node.js半年,從用Express開發自己的部落格到用Sails開發公司專案,深深被Sails震撼了。Sails是Balderdash團隊的產品,快速的專案構建、優秀的框架結構還有眾多的擴充套件,讓我有種相見恨晚的感覺。在Koa流行之前,個人認為Sails的使用者量還是挺可觀的。今天,我想寫一寫Sails那些讓我感動的地方,順便理順一下Sails的架構。

目錄

  • 一步搭建專案

  • 專案架構

  • ORM

  • MVC的實現

  • 路由

  • 安全

  • 日誌

  • 單元測試

  • WebSocket

一步搭建專案

在安裝了Node.js 和 Sails的環境下,只需要一條命令,就能夠搭建一個擁有完整架構的專案,儘管這很簡單,我還是覺得有必要說一下。

在已經安裝了Node.js和npm的前提下,首先你需要全域性下安裝Sails
$ sudo npm install sails -g

其次在一個空路徑下,新建一個專案
$ sails new newApp

最後,只需要前往專案路徑,把專案執行起來
$ cd testProject
$ sails lift

訪問 http://localhost:1337就能看到一個新的專案

new app

專案架構

.
├── api
│   ├── controllers
│   ├── models
│   ├── policies
│   ├── responses
│   └── services
├── views
├── assets
├── config
├── tasks
├── node_modules
├── package.json
├── Gruntfile.js
├── README.md
└── app.js

api/

api 目錄下是你要構建應用的核心所在,常說的MVC的設計結構就體現在這裡
api/controllers :控制層,該層是Http請求的入口。Sails官方建議該層只處理請求的轉發和頁面的渲染,具體的邏輯實現應該交給Service層。
api/models:模型層,在Sails中,對於Model採用的是充血模型,除了可以在模型中定於屬性之外,還可以定義包含邏輯處理的函式。在Sails中,所有Model都可以全域性性訪問。
api/policies:過濾層,該層在Controller層之前對Http請求做處理,在這一層中,可以定於一些規則來過濾Http請求,比如身份認證什麼的。
api/responses:http響應的方法都放這裡,例如伺服器錯誤、請求錯誤、404錯誤等,定義在responses資料夾裡面的方法,都會賦值到controller層的req物件中。
api/services:服務層,該層包含邏輯處理的方法,在Sails中,所有Service都可以全域性性訪問。

views/

檢視層,存放檢視模版檔案的地方,Sails預設是提供ejs模版引擎的,如果你願意,你可以換成jade、handlebars或者任何你喜歡的模版引擎。

assets/

資原始檔夾,在Sails啟動的時候,會啟動某一個Grunt任務,把assets資料夾裡的內容或壓縮或編譯或複製到根目錄下的.tmp目錄,這是前端可以直接通過路由訪問的資源,HTML、JS、CSS以及圖片等靜態資源都放在這裡了。

config/

配置資料夾,在Sails啟動的時候,會載入該資料夾裡的檔案,並賦值在全域性物件sails.config中,所以能夠在任何一個地方都能用到。在用Sails開發,會經常跟這個資料夾裡的檔案打交道,從config的構成很容易知道Sails都提供哪方面的功能。

tasks/

Sails自帶的專案自動化工具是Grunt,而Grunt的配置和任務註冊都放在這個資料夾裡了。這裡已經提供了通常會用到的CSS編譯、JS壓縮、檔案合併,更改檢測等等任務,當然如果沒有自己需要的,還能擴充套件。

app.js

Sails的啟動檔案,無論是$ sails lift命令或者$ npm start命令都會執行該檔案。

ORM

開發了Sails的團隊Balderdash,還開發了一套ORM框架:Waterline。
Waterline在Sails主要的舞臺是在/api/models目錄裡,在這裡定義的模型檔案,在Sails啟動的時候,都要經由Waterline的洗禮。
Waterline 是通過Adapter關聯資料庫的,不同的Adapter關聯不同的資料庫。
Waterline 能適配絕對部分資料庫,大致分類兩類,一類是官方團隊開發的 Adapter適配的,一類是民間開發者開發的Adapter適配的:
官方支援的:

  • PostgreSQL

  • MySQL

  • MongoDB

  • Redis

  • Disk

  • Memory

民間開發的:

  • SQLServer

  • OrientDB

  • Oracle

  • Cassandra

關於Waterline的更多資訊可以關注:
github:waterline
github:waterline-docs

MVC的實現

在這一段我想不僅僅要談論到Model層、View層和Controller層,我認為還有必要談到Service層、和Policy層。

Model層

模型檔案定義到/api/models中,由Waterline驅動,所有model都能全域性訪問。Sails提供命令列建立model的命令:$ sails generate model MODEL_NAME

View層

實現在/views中,除了預設提供的ejs模版引擎之外,還能更換成jade、handlebars等模版引擎

Controller層

/api/controller目錄裡,Sails中提供建立controller的命令:$ sails generate controller CONTROLLER_NAME。Sails也提供同時建立model和對應的Controller的命令:$ sails generate api API_NAME

Service層

/api/services目錄裡,存放自定義的服務,所有service都能夠全域性訪問,Sails官方的建議是把邏輯處理都放在該層中,Controller層只做路由的分發和輕邏輯的處理。

Policy層

/api/policies目錄裡,存放自定義的過濾器。該層是一條請求在到達Controller之前根據需求過濾請求的中間層。在/api/policies目錄中定義的檔案,還需要在config/policies.js檔案中為需求應用到某一過濾器的Action配置。

路由

Sails中要理解路由,首先要記得這個名詞blueprint,中文翻譯為:藍圖。我不知道官方是否解釋過為什麼要用個單詞,但以我的理解,Sails的blueprint是負責指揮每一條客戶端請求應該分配到伺服器端的哪個Action去,所以叫藍圖吧。
blueprint主要分為三種:RESTful routesShortcut routesAction routes

RESTful routes

當路徑諸如:/:modelIdentity 或者 /:modelIdentity/:id的時候,blueprint會根據HTTP的動作(GET、POST、DELETE、PUT等)來分配到相應的Controller下相應的Action來處理。例如一個POST請求/user會建立一個使用者,一個DELETE請求/user/123會刪除id為123的使用者。

Shortcut routes

這種路由主要是方便開發,請求的引數可以直接寫在請求路徑中,例如/user/create?name=joe會建立一個新的使用者,/user/update/1?name=mike會更新id為1的使用者的名字。shortcut routes在開發環境很便利,但是在生產環境下需要關閉。

Action routes

這種路由會自動的為Controller層的每一個Action建立一個路由,例如你的Controller層有一個FooController.js,裡面有一個Actionbar,那麼請求/foo/bar就會分配到barAction。

當然Sails也會提供自定義的路由,使用者可以在config/routes.jsconfig/polices.js這兩個配置檔案中選擇關閉或者開啟blueprint提供的路由,和定義自己的路由。

安全

要確保產品的安全性,要對幾種常見的攻擊和安全策略瞭如指掌,諸如CORS、CSRF、DDOS、XSS等。Sails對於常見的安全策略都有提供支援,且只需要通過相關的配置檔案就可以控制安全策略的等級。深入探討Web的安全策略,並不在本文的範疇內,日後我會以這個為題寫一篇文章聊聊Web的安全。
想要了解更多Sails的安全策略可以看看這裡:sails: security

日誌

Sails提供了一個全域性物件sails.log用來處理日誌資訊的輸出,日誌是分level的,在config/log.js中配置日誌輸出的level,而level的作用看下錶:

Priority level Log fns visible
0 silent N/A
1 error .error()
2 warn .warn(), .error()
3 debug .debug(), .warn(), .error()
4 info .info(), .debug(), .warn(), .error()
5 verbose .verbose(), .info(), .debug(), .warn(), .error()
6 silly .silly(), .verbose(), .info(), .debug(), .warn(), .error()

Sails的日誌管理預設是info層的,既會輸出.info(), .debug(), .warn(), .error()的資訊。

單元測試

Sails使用了mocha進行單元測試,在新建Sails專案的時候,沒有建立單元測試的資料夾,需要自己手動構造單元測試目錄,官方建議的目錄是這樣的:

.
├── api
├── assets
├── ...
├── test
│  ├── unit
│  │  ├── controllers
│  │  │  └── UsersController.test.js
│  │  ├── models
│  │  │  └── Users.test.js
│  │  └── ...
│  ├── fixtures
│  ├── ...
│  ├── bootstrap.test.js
│  └── mocha.opts
└── views

而我在單元測試常用的組合是:mochashouldsupertest、 istanbul 
其中should是提供斷言,supertest是用於測試Controller層的時候偽造http請求的,而istanbul則是提供測試程式碼覆蓋率的。
關於怎麼在Sails中編寫測試程式碼,可以參考 sails:testing

WebSocket

對於有即時性通訊需求的Web應用,我們會用Socket,Sails也為這方面提供了支援。在客戶端提供js檔案:sails.io.js,而在伺服器端提供全域性物件:sails.sockets。通過這兩個物件,就可以進行客戶端和伺服器端即時性通訊的開發了。
Sails預設會啟動WebSocket功能,在客戶端訪問伺服器端的時候,會自動嘗試在同域名下連線socket。
值得注意的是,這樣會對AngularJS、EmberJS等前端MVVC開發產生一些障礙。
比如進行AngularJS開發的時候,我們在http://localhost:9000跑AngularJS專案,而伺服器端卻跑在http://localhost:1337
當訪問http://localhost:9000的時候,sails.io.js會嘗試於當前路徑下進行socket連線,也就是http://localhost:9000,這時會出錯,因為伺服器是跑在http://localhost:1337的。
在開發的時候要解決這樣的問題的時候,我們只需要在AngularJS這邊引入sails.io.js之後定義連線路徑就行了:

<script src="scripts/lib/sails.io.js"></script>
<script>io.sails.url = "http://localhost:1337";</script>

結語

可以說Sails涵蓋了Web開發中會遇到的絕大部分需求和問題,如果深入研究Sails的話,是受益匪淺的。


如果本文對您有用
請不要吝嗇你們的Follow與Start
這會大大支援我們繼續創作

「Github」
MZMonster :@MZMonster
JC_Huang :@JerryC8080

相關文章