讓 LoopBack 來做:您夢寐以求的 Node API 框架演練
已發表: 2022-03-11不用說 Node.js 在應用程序開發中的日益流行。 自 2011 年以來,eBay 一直在運行生產 Node API 服務。PayPal 正在積極地在 Node.js 中重建他們的前端。 沃爾瑪的移動網站已成為最大的節點應用程序,流量明智。 2014 年感恩節週末,沃爾瑪服務器處理了 15 億個請求,其中 70% 通過移動設備交付並由 Node.js 提供支持。 在開發方面,Node 包管理器 (npm) 繼續快速增長,最近超過 150,000 個託管模塊。
雖然 Ruby 有 Rails,Python 有 Django,但主要的 Node 應用程序開發框架尚未建立。 但是,有一個強大的競爭者正在獲得動力:LoopBack,一個由加利福尼亞州聖馬特奧的 StrongLoop 公司構建的開源 API 框架。 StrongLoop 是最新 Node 版本的重要貢獻者,更不用說 Express 的長期維護者了,Express 是現存最流行的 Node 框架之一。
讓我們通過將所有內容付諸實踐並構建示例應用程序來更深入地了解 LoopBack 及其功能。
什麼是 LoopBack 以及它如何與 Node 一起使用?
LoopBack 是一個用於創建 API 並將它們與後端數據源連接起來的框架。 它建立在 Express 之上,可以採用數據模型定義並輕鬆生成功能齊全的端到端 REST API,任何客戶端都可以調用該 API。
LoopBack 帶有一個內置客戶端API Explorer 。 我們將使用它,因為它可以更容易地查看我們的工作結果,並且我們的示例可以專注於構建 API 本身。
你當然需要在你的機器上安裝 Node 來跟隨。 在這裡得到它。 npm 附帶它,因此您可以輕鬆安裝必要的軟件包。 讓我們開始吧。
創建骨架
我們的應用程序將管理想要向可能需要它們的人捐贈禮物或他們不再需要的東西的人。 因此,用戶將是捐贈者和接收者。 捐贈者可以創建新禮物並查看禮物列表。 接收者可以查看所有用戶的禮物列表,並可以領取任何無人領取的禮物。 當然,我們可以將捐贈者和接收者構建為同一實體(用戶)上的不同角色,但讓我們嘗試將它們分開,以便了解如何在 LoopBack 中構建關係。 這個開創性的應用程序的名稱將是Givesomebody 。
通過 npm 安裝 StrongLoop 命令行工具:
$ npm install -g strongloop
然後運行 LoopBack 的應用程序生成器:
$ slc loopback _-----_ | | .--------------------------. |--(o)--| | Let's create a LoopBack | `--------- | application! | ( _U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ` |° Y ` ? What's the name of your application? Givesomebody
讓我們添加一個模型。 我們的第一個模型將被稱為 Gift。 LoopBack 將詢問數據源和基類。 由於我們還沒有設置數據源,我們可以放db (memory)
。 基類是一個自動生成的模型類,我們想在這種情況下使用PersistedModel
,因為它已經包含了我們所有常用的 CRUD 方法。 接下來,LoopBack 詢問它是否應該通過 REST 公開模型(是),以及 REST 服務的名稱。 在此處按 enter 以使用默認值,即模型名稱的複數形式(在我們的示例中為gifts
)。
$ slc loopback:model ? Enter the model name: Gift ? Select the data-source to attach Gift to: (Use arrow keys) ❯ db (memory) ? Select model's base class: (Use arrow keys) Model ❯ PersistedModel ? Expose Gift via the REST API? (Y/n) Yes ? Custom plural form (used to build REST URL):
最後,我們給出屬性的名稱、它們的數據類型和必需/非必需標誌。 禮物將具有name
和description
屬性:
Let's add some Gift properties now. Enter an empty property name when done. ? Property name: name invoke loopback:property ? Property type: (Use arrow keys) ❯ string ? Required? (y/N)Yes
輸入一個空屬性名稱以指示您已完成定義屬性。
模型生成器將在應用程序的common/models
中創建兩個定義模型的文件: gift.json
和gift.js
JSON 文件指定有關實體的所有元數據:屬性、關係、驗證、角色和方法名稱。 JavaScript 文件用於定義附加行為,並指定要在某些操作(例如,創建、更新或刪除)之前或之後調用的遠程掛鉤。
另外兩個模型實體將是我們的 Donor 和 Receiver 模型。 我們可以使用相同的過程創建它們,只是這次我們將User
作為基類。 它將為我們提供一些屬性,如username
、 password
、 email
等。 例如,我們可以只添加名稱和國家/地區,以獲得完整的實體。 對於收件人,我們也想添加送貨地址。
項目結構
我們來看看生成的項目結構:
三個主要目錄是: - /server
– 包含節點應用程序腳本和配置文件。 - /client
– 包含 .js、.html、.css 和所有其他靜態文件。 - /common
– 此文件夾對服務器和客戶端都是通用的。 模型文件放在這裡。
以下是每個目錄內容的詳細分類,取自 LoopBack 文檔:
文件或目錄 | 描述 | 如何在代碼中訪問 |
---|---|---|
頂級應用目錄 | ||
package.json | 標準 npm 包規範。 見 package.json | 不適用 |
/server 目錄 - 節點應用程序文件 | ||
server.js | 主應用程序文件。 | 不適用 |
config.json | 應用程序設置。 請參閱 config.json。 | app.get('setting-name') |
datasources.json | 數據源配置文件。 請參閱 datasources.json。 有關示例,請參閱創建新數據源。 | app.datasources['datasource-name'] |
model-config.json | 模型配置文件。 請參閱模型配置.json。 有關詳細信息,請參閱 將模型連接到數據源。 | 不適用 |
middleware.json | 中間件定義文件。 有關更多信息,請參閱定義中間件。 | 不適用 |
/boot 目錄 | 添加腳本以執行初始化和設置。 請參閱引導腳本。 | 腳本按字母順序自動執行。 |
/client 目錄 - 客戶端應用程序文件 | ||
README.md | LoopBack 生成器以 markdown 格式創建空的 README 文件。 | 不適用 |
其他 | 添加您的 HTML、CSS、客戶端 JavaScript 文件。 | |
/common 目錄 - 共享應用程序文件 | ||
/models 目錄 | 自定義模型文件:
| 節點:myModel = app.models.myModelName |
建立關係
在我們的示例中,我們有一些重要的關係需要建模。 一個 Donor 可以捐贈許多 Gifts,這賦予了關係Donor has many Gift 。 一個 Receiver 也可以收到很多 Gifts,所以我們也有關係Receiver has many Gift 。 另一方面, Gift 屬於 Donor ,如果 Receiver 選擇接受,也可以屬於 Receiver 。 讓我們把它放到 LoopBack 的語言中。
$ slc loopback:relation ? Select the model to create the relationship from: Donor ? Relation type: has many ? Choose a model to create a relationship with: Gift ? Enter the property name for the relation: gifts ? Optionally enter a custom foreign key: ? Require a through model? No
注意沒有直通模式; 我們只是持有對禮物的引用。
如果我們對Receiver重複上述過程,並在Gift中添加兩個屬於關係,我們將在後端完成我們的模型設計。 LoopBack 自動更新模型的 JSON 文件,以準確表達我們剛剛通過這些簡單對話框所做的事情:
// common/models/donor.json ... "relations": { "gifts": { "type": "hasMany", "model": "Gift", "foreignKey": "" } }, ...
添加數據源
現在讓我們看看如何附加一個真實的數據源來存儲我們所有的應用程序數據。 出於本示例的目的,我們將使用 MongoDB,但 LoopBack 具有連接 Oracle、MySQL、PostgreSQL、Redis 和 SQL Server 的模塊。
首先,安裝連接器:
$ npm install --save loopback-connector-mongodb
然後,將數據源添加到您的項目中:
$ slc loopback:datasource ? Enter the data-source name: givesomebody ? Select the connector for givesomebody: MongoDB (supported by StrongLoop)
下一步是在server/datasources.json
中配置您的數據源。 將此配置用於本地 MongoDB 服務器:
... "givesomebody": { "name": "givesomebody", "connector": "mongodb", "host": "localhost", "port": 27017, "database": "givesomebody", "username": "", "password": "" } ...
最後,打開server/model-config.json
並將我們想要在數據庫中持久化的所有實體的datasource
更改為"givesomebody"
。
{ ... "User": { "dataSource": "givesomebody" }, "AccessToken": { "dataSource": "givesomebody", "public": false }, "ACL": { "dataSource": "givesomebody", "public": false }, "RoleMapping": { "dataSource": "givesomebody", "public": false }, "Role": { "dataSource": "givesomebody", "public": false }, "Gift": { "dataSource": "givesomebody", "public": true }, "Donor": { "dataSource": "givesomebody", "public": true }, "Receiver": { "dataSource": "givesomebody", "public": true } }
測試你的 REST API
是時候看看我們到目前為止所構建的內容了! 我們將使用很棒的內置工具API Explorer ,它可以用作我們剛剛創建的服務的客戶端。 讓我們嘗試測試 REST API 調用。
在一個單獨的窗口中,使用以下命令啟動 MongoDB:
$ mongod
運行應用程序:
$ node .
在您的瀏覽器中,轉到http://localhost:3000/explorer/
。 您可以通過可用操作列表查看您的實體。 嘗試通過 POST /Donors
調用添加一位捐贈者。
API Explorer非常直觀; 選擇任何公開的方法,右下角會顯示相應的模型架構。 在data
文本區域,可以編寫自定義 HTTP 請求。 填寫好請求後,點擊“Try it out”按鈕,下面會顯示服務器的響應。

用戶認證
如上所述,LoopBack 預構建的實體之一是 User 類。 用戶擁有登錄和註銷方法,並且可以綁定到一個AccessToken實體,該實體保存特定用戶的令牌。 事實上,一個完整的用戶身份驗證系統已經準備好開箱即用。 如果我們嘗試通過API Explorer調用/Donors/login
,我們得到的響應如下:
{ "id": "9Kvp4zc0rTrH7IMMeRGwTNc6IqNxpVfv7D17DEcHHsgcAf9Z36A3CnPpZJ1iGrMS", "ttl": 1209600, "created": "2015-05-26T01:24:41.561Z", "userId": "" }
id
實際上是AccessToken的值,自動生成並保存在數據庫中。 正如您在此處看到的,可以設置訪問令牌並將其用於每個後續請求。
遠程方法
遠程方法是模型的靜態方法,通過自定義 REST 端點公開。 遠程方法可用於執行 LoopBack 的標準模型 REST API 未提供的操作。
除了我們開箱即用的 CRUD 方法之外,我們還可以添加任意數量的自定義方法。 所有這些都應該進入[model].js
文件。 在我們的例子中,讓我們在 Gift 模型中添加一個遠程方法來檢查禮物是否已被保留,並列出所有未保留的禮物。
首先,讓我們為模型添加一個名為reserved
的附加屬性。 只需將其添加到gift.json
的屬性中:
... "reserved": { "type": "boolean" } ...
gift.js
中的遠程方法應該如下所示:
module.exports = function(Gift) { // method which lists all free gifts Gift.listFree = function(cb) { Gift.find({ fields: { reserved: false } }, cb); }; // expose the above method through the REST Gift.remoteMethod('listFree', { returns: { arg: 'gifts', type: 'array' }, http: { path: '/list-free', verb: 'get' } }); // method to return if the gift is free Gift.isFree = function(id, cb) { var response; Gift.find({ fields: { id: id } }, function(err, gift) { if (err) return cb(err); if (gift.reserved) response = 'Sorry, the gift is reserved'; else response = 'Great, this gift can be yours'; }); cb(null, response); }; // expose the method through REST Gift.remoteMethod('isFree', { accepts: { arg: 'id', type: 'number' }, returns: { arg: 'response', type: 'string' }, http: { path: '/free', verb: 'post' } }); };
因此,要確定是否有特定禮物可用,客戶端現在可以向/api/Gifts/free
發送 POST 請求,並傳入相關禮物的id
。
遠程掛鉤
有時需要在遠程方法之前或之後執行某些方法。 您可以定義兩種遠程掛鉤:
-
beforeRemote()
在遠程方法之前運行。 -
afterRemote()
在遠程方法之後運行。
在這兩種情況下,您都提供了兩個參數:一個與您要“掛鉤”您的函數的遠程方法匹配的字符串,以及回調函數。 遠程掛鉤的大部分功能是字符串可以包含通配符,因此它可以由任何匹配方法觸發。
在我們的例子中,讓我們設置一個掛鉤,以便在創建新的 Donor 時將信息打印到控制台。 為了實現這一點,讓我們在donor.js
中添加一個“before create”鉤子:
module.exports = function(Donor) { Donor.beforeRemote('create', function(context, donor, next) { console.log('Saving new donor with name: ', context.req.body.name); next(); }); };
使用給定的context
調用請求,並且在鉤子運行後調用中間件(下面討論)中的next()
回調。
訪問控制
LoopBack 應用程序通過模型訪問數據,因此控制對數據的訪問意味著定義對模型的限制; 也就是說,指定誰或什麼可以讀寫數據或在模型上執行方法。 LoopBack 訪問控制由訪問控制列表或 ACL 確定。
讓我們允許未登錄的捐贈者和接收者查看禮物,但只有登錄的捐贈者才能創建和刪除它們。
$ slc loopback:acl
首先,讓我們拒絕所有人訪問所有端點。
? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: All (match all types) ? Select the role: All users ? Select the permission to apply: Explicitly deny access
接下來,讓大家閱讀Gift模型:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Read ? Select the role: All users ? Select the permission to apply: Explicitly grant access
然後,我們希望允許經過身份驗證的用戶創建禮物:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: A single method ? Enter the method name: create ? Select the role: Any authenticated user ? Select the permission to apply: Explicitly grant access
最後,讓我們允許禮物的所有者進行任何更改:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Write ? Select the role: The user owning the object ? Select the permission to apply: Explicitly grant access
現在,當我們查看gift.json
時,一切都應該到位:
"acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" } ],
這裡有一個重要說明: $authenticated
是一個預定義角色,對應於系統中的所有用戶(捐贈者和接收者),但我們只想允許捐贈者創建新禮物。 因此,我們需要一個自定義角色。 由於 Role 是我們開箱即用的另一個實體,我們可以利用它的 API 調用在 boot 函數中創建$authenticatedDonor
角色,然後只需修改pricipalId
中的gift.json
。
有必要創建一個新文件server/boot/script.js
並添加以下代碼:
Role.create({ name: 'authenticatedDonor' }, function(err, role) { if (err) return debug(err); })
RoleMapping 實體將角色映射到用戶。 確保 Role 和 RoleMapping 都通過 REST 公開。 在server/model-config.json
中,檢查 Role 實體的"public"
是否設置為true
。 然後在donor.js
中,我們可以編寫一個“before create”鉤子來映射RoleMapping POST API 調用中的userID
和roleID
。
中間件
中間件包含向 REST 端點發出請求時執行的函數。 由於 LoopBack 基於 Express,它使用 Express 中間件和一個額外的概念,稱為“中間件階段”。 階段用於明確定義調用中間件中函數的順序。
以下是 LoopBack 文檔中提供的預定義階段列表:
- initial - 中間件可以運行的第一個點。
- session - 準備會話對象。
- auth - 處理身份驗證和授權。
- parse - 解析請求正文。
- routes - 實現應用程序邏輯的 HTTP 路由。 通過 Express API app.use、app.route、app.get(和其他 HTTP 動詞)註冊的中間件在此階段開始時運行。 將此階段也用於 loopback/server/middleware/rest 或 loopback-explorer 等子應用程序。
- 文件- 提供靜態資產(請求在此處訪問文件系統)。
- final - 處理錯誤和對未知 URL 的請求。
每個階段包含三個子階段。 例如,初始階段的子階段是:
- 初始:之前
- 最初的
- 初始:之後
讓我們快速瀏覽一下我們的默認 middleware.json:
{ "initial:before": { "loopback#favicon": {} }, "initial": { "compression": {}, "cors": { "params": { "origin": true, "credentials": true, "maxAge": 86400 } } }, "session": { }, "auth": { }, "parse": { }, "routes": { }, "files": { }, "final": { "loopback#urlNotFound": {} }, "final:after": { "errorhandler": {} } }
在初始階段,我們調用loopback.favicon()
( loopback#favicon
是該調用的中間件 ID)。 然後,調用第三方 npm 模塊compression
和cors
(帶或不帶參數)。 在最後階段,我們還有兩個電話。 urlNotFound
是 LoopBack 調用, errorhandler
是第三方模塊。 這個例子應該證明可以像使用外部 npm 模塊一樣使用許多內置調用。 當然,我們總是可以創建自己的中間件並通過這個 JSON 文件調用它們。
loopback-boot
最後,讓我們提到一個模塊,它導出初始化應用程序的boot()
函數。 在server/server.js
中,您將找到以下引導應用程序的代碼:
boot(app, __dirname, function(err) { if (err) throw err; // start the server if `$ node server.js` if (require.main === module) app.start(); });
該腳本將搜索server/boot
文件夾,並按字母順序加載它在其中找到的所有腳本。 因此,在server/boot
中,我們可以指定任何應該在啟動時運行的腳本。 一個例子是explorer.js
,它運行API Explorer ,我們用來測試我們的 API 的客戶端。
結論
在我離開你之前,我想提一下 StrongLoop Arc,它是一個圖形 UI,可以用作slc
命令行工具的替代品。 它還包括用於構建、分析和監控 Node 應用程序的工具。 對於那些不喜歡命令行的人來說,這絕對值得一試。 但是,StrongLoop Arc 即將被棄用,其功能正在集成到 IBM API Connect Developer Toolkit 中。
一般來說,LoopBack 可以為您節省大量手動工作,因為您可以立即使用很多東西。 它使您可以專注於特定於應用程序的問題和業務邏輯。 如果您的應用程序基於 CRUD 操作並操縱預定義的實體,如果您厭倦了重寫用戶的身份驗證和授權基礎設施,而大量開發人員已經在您之前編寫了該基礎設施,或者您想利用一個偉大的 Web 框架的所有優勢,例如Express,然後使用 LoopBack 構建您的 REST API 可以讓您的夢想成真。 很簡單的!