從頭建立一套 web-based 服務架構 (9) 開發 Loopback App
繼昨天介紹了 API Server 的基本架構後,今天要來簡單介紹 Loopback 為了快速開發而提供的諸多方便功能。
Config
Loopback 的概念處處充滿 declarative,也就是以 JSON 檔設定需要啟用的功能和它們的設定;這和自行呼叫程式碼進行設定的 imperative 相比,能夠更簡易、不出錯地進行環境設定,但也較不具彈性。
這些設定檔會放在 src/config
資料夾內。
除了使用 JSON 格式,所有的設定檔均能使用一般的 JavaScript 格式,寫成 CommonJS module 並於 module.exports
輸出和 JSON 檔相同的 JavaScript 物件即可。
這個作法可以用來和環境變數整合,讓有需要的設定值能夠於佈署時期決定。
以下介紹一些常用的設定值:
config.json
: Server instance 本身的相關設定restApiRoot
: 設定 RESTful API 的 root path,一般而言不會使用 server 本身的根路徑/
,而是用一兩層 path 進行包裝,例如/api
或/api/v1
。host
和port
用來傳給 Node.js 原生的http
module 中的server.listen
API。remoting.rest.normalizeHttpPath
把 URL path 正規化(underscore 變 dash、Camel case 變 Kebab case)remoting.rest.xml
: 設定是否要接受appliaction/xml
類型的Content-Type
remoting.json
: 傳給bodyParser
用的參數設定remoting.urlencoded
: 傳給bodyParser.urlencoded
用的參數設定
datasources.json
: Loopback 允許設定複數的 ORM/ODM 綁定,支援範圍包括 MySQL、MongoDB 甚至 in-memory 的 redis 均可。不同的 datasource 類型以connector
做區分,並需要安裝對應的loopback-connector-*
套件。component-config.json
: Loopback 可以額外安裝不同的 plugin(稱為 Loopback component),而 Loopback 會用這個檔案來設定 component,例如 Swagger UI 使用的loopback-component-explorer
。model-config.json
: 這裡需要設定的是所有在src/common/models
定義的 model 資料所對應的 datasource,也可以用public
設定是否要為這些 model 開放 RESTful API。
Bootstrapping Scripts
loopback-boot
套件會讀取使用者指定的路徑,將裡面所有的 bootstrapping scripts 依照檔案名稱順序全部執行一次。常用的 bootstrapping script 包括:
- 讀取 roles 設定
- 替這個 API Server 建立一個 default 的 admin 使用者
- 進行 database migration
Model Definition
Loopback 最棒的價值在於直接定義好 model 就能自動生成 RESTful API endpoints 和 database schema。
這些 model definition 會放在 src/common/models
,每個 JSON 檔對應一個 model。
Roles
Roles 是用於 Access Control List (ACL),可以非常精細的控制不同的 API 的存取權限。
Remote Methods
除了 Loopback 預設生成的 RESTful API,我們當然能自行宣告客製化的 API 掛載到 model 上,這些客製化的部份稱為 remote method。
宣告 remote method 需要在 src/common/model
宣告 model 的 JSON 檔時,一起宣告一個 .js
的 JavaScript 檔案(例如 Assignment.json
和 Assignment.js
)。
Loopback 會在 bootstrapping 時期將 Model 類別作為參數傳入 JS 檔 export 出來的 function,讓開發者在 function 內進行 remote method 的宣告。
通常要宣告一個 remote method 有兩個步驟:
1) 定義 method 本體 的 function。
這個 function 的輸入參數是標準的 Node.js callback style,由前面的輸入參數和最後一個 callback
參數構成,所有的回傳值也要以 callback(err, result)
傳回。
在 function 內部就能透過其他的 Model method 來對 datasource 操作,取得想要的結果後再以 callback
回傳。
Method 有分 static method
和 object method
兩種,顧名思義就是分別定義於 Model class 和 Model instance 上的 method,兩者可分別用 Model.aMethod
和 Model.prototype.aMethod
進行定義。
2) 呼叫 Model.remoteMethod
並傳入 remote method 的設定值。
在設定裡面可以細部定義這個 API 的細節,包括 API 的 verb、URL path、有哪些輸入參數(要傳給 (1) 定義的 function 用的)、輸入參數的來源(query string、URL path argument、request header/body 等)、response body 的結構定義等。
以下用 user model 為例,在 user instance 上宣告一個改變密碼的 remote method:
module.exports = User => {
User.prototype.changePassword = function changePassword(newPassword, callback) {
this.updateAttributeAsync('password', newPassword)
.then(user => callback(null, { message: 'success' }))
.catch(err => callback(err));
};
User.remoteMethod('changePassword', {
isStatic: false,
accepts: [
{ arg: 'newPassword', type: 'string' }
],
returns: {
arg: 'info', type: 'object', root: true
},
http: { path: '/password', verb: 'put' },
description: 'Change password of a user'
});
};
用 loopback-component-passport 設定 local login
Loopback 在 loopback-component-passport
套件實作了基於 access token 的登入流程,提供 user、access token 等 model 後便能直接使用:
import { PassportConfigurator } from 'loopback-component-passport';
app.use(loopback.token({
model: app.models.accessToken
}));
const passportConfigurator = new PassportConfigurator(app);
passportConfigurator.init(true);
passportConfigurator.setupModels({
userModel: app.models.user,
userIdentityModel: app.models.userIdentity,
userCredentialModel: app.models.userCredential
});
passportConfigurator.configureProvider('local', {
authScheme: 'local',
provider: 'local',
module: 'passport-local',
usernameField: 'username',
passwordField: 'password',
authPath: '/auth/local'
});
整合 Swagger UI
Swagger UI 是個功能強大的 API「瀏覽器」。這個介面會將所有 RESTful API 以 Model 為分隔各自列出,並條列每個 API 的細節,最重要的是可以直接在這個 web app 上呼叫這些 API 進行手動測試。
Loopback 使用 loopback-component-explorer
將 Swagger UI 整合到 Loopback 系統內,稱為 Strongloop API Explorer。
在 component-config.json
宣告設定細節:
{
"loopback-component-explorer": {
"mountPath": "/explorer",
"apiInfo": {
"title": "API explorer",
"description": "API explorer"
}
}
}
以下是 Strongloop API Explorer 的畫面:
從 API 測試區域也能看到根據 remote method 的設定而生成的 UI: