Loopback 入门教程 下
上篇里,我们只是把 LoopBack 3 当成一个快速搭 CRUD 接口的工具,一个 CoffeeShop 模型,
一套 MySQL 后端,再配上 API Explorer,能方便地查店铺列表、看 REST 响应。
如果你只想写个内部小服务,这已经够用。但真实世界的应用很少这么简单,通常还会有用户系统、评论、权限、以及前端页面。 这也是官方在 Getting started part II 想做的事:用一套 Coffee Shop Reviews 小应用,把 LoopBack 的几块常用能力拼在一起: 多数据源、模型关系、ACL、remote hook,再加一个基于 AngularJS SDK 的前端。
所以这篇下篇,我们在上篇的基础上,一步步把这个小站搭起来:先接上第二个 MongoDB 数据源,
再补上 Review / Reviewer 模型和它们之间的关系,接着用 ACL 和 remote hook 把“谁能做什么”说清楚,
最后再让前端跑起来,把所有东西串成一个可以点点点的完整应用。
Coffee Shop Reviews 应用需求分析
官方示例的目标是做一个“咖啡店版 Yelp”。应用的大致结构是:
-
数据源
- MySQL:继续存上篇中的
CoffeeShop数据。 - MongoDB:专门存
Reviewer(用户)和Review(评论)。
- MySQL:继续存上篇中的
-
模型(Models)
CoffeeShop:咖啡店(上篇已经有)。Review:某个咖啡店的一条评论。Reviewer:评论人,继承 LoopBack 内置的User模型,用它来完成注册、登录、注销这些操作。
-
模型关系(Relations)
- 一个 CoffeeShop 有很多 Review。
- 一个 CoffeeShop 有很多 Reviewer。
- 一个 Review 属于一个 CoffeeShop。
- 一个 Review 属于一个 Reviewer。
- 一个 Reviewer 有很多 Review。
-
权限规则(ACL)
- 所有人都能看评论。
- 想写 / 改 / 删评论,必须先登录。
- 任何人都可以注册账号并登录/登出。
- 用户只能改自己的评论,不能顺手把评论对应的咖啡店改掉。
-
前端
- 用 AngularJS SDK 自动生成访问 REST API 的客户端服务,再配上一个简单的单页应用 UI。
这一篇,就是一步一步把这套东西复刻一遍。
前期准备
先把官方的 “loopback-getting-started-intermediate” 仓库拉下来跑一跑,心里有个大致画面:
git clone https://github.com/strongloop/loopback-getting-started-intermediate.git
cd loopback-getting-started-intermediate
npm install
node .
# 控制台里会看到:
# Web server listening at: http://0.0.0.0:3000/
# Browse your REST API at http://0.0.0.0:3000/explorer
# > models created sucessfully
在浏览器打开:
http://0.0.0.0:3000/:前端页面。http://0.0.0.0:3000/explorer:API Explorer。
你会看到:

- 首页是一个 Coffee Shop 列表 + 登录入口。
- 登录后可以新增评论、查看自己的评论、查看所有评论。
你可以选择:
- 自己从上篇的项目继续搭:
cd <你的上篇项目目录>; - 或者偷个懒:直接 clone 上篇起点仓库
loopback-getting-started当作基础,然后按下面步骤改。
后面的命令我默认你已经在自己的项目目录里,例如:
cd loopback-getting-started # 或你的上篇项目
添加二个数据源
上篇里,我们只有一个 MySQL 数据源,用来存咖啡店。 现在要再加一个 MongoDB 数据源,用于 Reviewer / Review。
用 CLI 新建数据源配置
在项目根目录执行(LB3 CLI):
lb datasource
根据提示大致填成这样:
- 数据源名称:
mongoDs - 选择连接器:
MongoDB - Host:
demo.strongloop.com(官方示例用的演示服务器,你也可以换成本地 Mongo) - Port:
27017 - Database:
getting_started_intermediate(随你,只要后面配置一致) - 用户名:
demo - 密码:
L00pBack
执行完后,server/datasources.json 会多出一个类似这样的片段:
"mongoDs": {
"name": "mongoDs",
"connector": "mongodb",
"host": "demo.strongloop.com",
"port": 27017,
"database": "getting_started_intermediate",
"username": "demo",
"password": "L00pBack"
}
注意:
真正写业务时,别把密码硬编码在 repo 里,用环境变量或配置文件区分开发 / 测试 / 生产会舒服很多。
如果你有自己的 MongoDB(比如本机),就把 host/database/用户名密码改成自己的。
添加 Review 和 Reviewer 模型
现在 CoffeeShop 还孤零零一张表,我们来加上 Review 和 Reviewer 两个模型,并把假数据灌进去。
评论(Review)
执行:
lb model
按提示填:
- Model name:
Review - Data source:选刚才的
mongoDs (mongodb) - Base class:
PersistedModel - Expose via REST:Yes
- Common/server only:Common
属性字段可以设成:
| 字段名 | 类型 | 是否必填 |
|---|---|---|
| date | date | 是 |
| rating | number | 否 |
| comments | string | 是 |
生成完之后,会有:
common/models/review.json:模型定义(字段、关系、ACL)。common/models/review.js:以后放业务逻辑(remote hook 就写这里)。
Reviewer
再来一个:
lb model
选择项大致是:
- Model name:
Reviewer - Data source:
mongoDs - Base class:User(这一步很关键)
- Expose via REST:Yes
- Common/server only:Common
不需要再添加属性——登录、密码、email 等字段都从 User 继承了。
很多应用里的“用户子类”(比如 Reviewer、Admin、Customer), 其实都可以直接继承 LoopBack 内置 User,少写很多轮子的代码。
更新 boot 脚本
官方给了一个 server/boot/create-sample-models.js,里面用 automigrate 和 async.parallel 做了几件事:
- 对 MySQL 的
CoffeeShop做 automigrate,并插入几家示例咖啡店。 - 对 MongoDB 的
Reviewer做 automigrate,并创建几个示例用户(比如foo@bar.com等)。 - 对 MongoDB 的
Review做 automigrate,并自动生成一些评论,帮你填满数据列表。
你可以照着官方脚本,把自己项目里的 create-sample-models.js 替换掉,再安装它用到的依赖:
npm install --save async
重启项目:
node .
# 控制台里应该能看到:
# > models created sucessfully
这时 MySQL 和 MongoDB 里都已经有数据了。
把模型串起来
有了三张表,下一步就是搞清楚它们之间怎么互相引用。
在 Coffee Shop Reviews 应用里,关系是这样的:
- CoffeeShop hasMany Review
- CoffeeShop hasMany Reviewer
- Review belongsTo CoffeeShop
- Review belongsTo Reviewer(外键叫
publisherId) - Reviewer hasMany Review(也用
publisherId做外键)
用 lb relation 把关系写进模型
在项目根目录反复执行:
lb relation
每一次调用都描述一条关系,比如(简化版描述):
-
CoffeeShop 有很多 Review
- From model:
CoffeeShop - Relation type:
hasMany - To model:
Review - Property name:
reviews
- From model:
-
CoffeeShop 有很多 Reviewer
- From model:
CoffeeShop - Relation type:
hasMany - To model:
Reviewer - Property name:
reviewers
- From model:
-
Review 属于一个 CoffeeShop
- From model:
Review - Relation type:
belongsTo - To model:
CoffeeShop - Property name:
coffeeShop
- From model:
-
Review 属于一个 Reviewer
- From model:
Review - Relation type:
belongsTo - To model:
Reviewer - Property name:
reviewer - Foreign key:
publisherId
- From model:
-
Reviewer 有很多 Review
- From model:
Reviewer - Relation type:
hasMany - To model:
Review - Property name:
reviews - Foreign key:
publisherId
- From model:
生成完成后,review.json 和 reviewer.json 里会多出 relations 段,清楚地把这些关系写进去。
注意:
- 一个模型既
belongsTo又被hasMany,通常就是“一对多”的两端。 - 重用
publisherId做双向关系(Reviewer ↔ Review)能让数据结构更干净,以后查询“某个用户的所有评论”会非常顺。
加上权限
有了用户、有了数据,接下来就要把“谁能干什么”说清楚。
这一节的目标,是用 ACL 实现前面提到的几条规则:
- 所有人可以读评论(匿名访问 GET /Reviews 没问题)。
- 想创建 / 修改 / 删除评论,必须登录。
- 登录用户可以 CRUD 自己的评论,但不能改别人写的,也不能改 CoffeeShop 本身。
用 lb acl 定义规则
还是在项目根目录,多次运行:
lb acl
每次执行定义一条 ACL 记录,几条主要规则可以这么配:
-
先全盘禁用
- Model:
All existing models - Scope:All methods and properties
- Access type:All
- Role:All users (
$everyone) - Permission:Explicitly deny
- Model:
-
任何人都能读评论
- Model:
Review - Scope:All methods and properties
- Access type:Read
- Role:All users (
$everyone) - Permission:Allow
- Model:
-
登录用户可以读 CoffeeShop 列表
- Model:
CoffeeShop - Scope:All methods and properties
- Access type:Read
- Role:Any authenticated user (
$authenticated) - Permission:Allow
- Model:
-
登录用户可以调用写评论(Review.create)
- Model:
Review - Scope:Single method
- Method name:
create - Role:Any authenticated user
- Permission:Allow
- Model:
-
评论作者可以修改 / 删除自己的评论
- Model:
Review - Scope:All methods and properties(写操作)
- Access type:Write
- Role:The user owning the object (
$owner) - Permission:Allow
- Model:
最终在 common/models/review.json 里,你会看到一个 acls 数组,大致长这样:
"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" },
{ "accessType": "WRITE","principalType": "ROLE","principalId": "$owner", "permission": "ALLOW" }
]
建议:
- 先 deny all,再按功能慢慢放行。
- 这样不会因为漏配某条规则,让接口在你没注意的情况下暴露出去。
添加业务逻辑
现在的 REST API 还缺点业务逻辑:
- 用户创建评论时,其实不应该手写
date与publisherId。 - 这两个字段完全可以由服务端自动填。
这就是 remote hook 的用武之地。官方示例让我们在 Review 上挂一个 beforeRemote('create'):
每次调用 REST 的 create 方法时,自动填充日期和发布人。
在 review.js 里加钩子
编辑 common/models/review.js,写一个类似下面的实现:
module.exports = function (Review) {
Review.beforeRemote("create", function (ctx, unused, next) {
const data = ctx.args.data || {};
// 自动把评论时间设为当前时间
data.date = Date.now();
// 从 accessToken 里拿当前用户 ID,当成 publisherId
if (ctx.req && ctx.req.accessToken) {
data.publisherId = ctx.req.accessToken.userId;
}
ctx.args.data = data;
next();
});
};
这段逻辑做了两件事:
- 在真正写入数据库前,把评论时间补上。
- 把当前登录用户的
userId写进publisherId字段,和前面关系里配置的外键对上。
对比一下 operation hook:
- remote hook:只在“通过 REST 调用某个方法”时触发。
- operation hook:无论是 REST、脚本还是其它方式,只要模型在做 create/save/update 这些操作,就会触发。 在这里我们只关心 HTTP API 的调用,所以选 remote hook 正好合适。
用 AngularJS SDK 做一个简单前端
后端已经比较像样了,最后一步是给它配一个浏览器端 UI。
LoopBack 提供了 AngularJS SDK,可以帮你自动生成和模型一一对应的 Angular $resource 服务,省掉一大堆手写 HTTP 调用的重复劳动。
用 lb-ng 生成客户端服务
AngularJS SDK 自带一个 lb-ng 命令,用来扫描你的 server/server.js,然后生成一个 lb-services.js 文件(里面是各个模型的 Angular 服务)。
如果还没装过:
npm install -g loopback-sdk-angular-cli
在项目根目录创建目录:
mkdir -p client/js/services
然后执行类似下面的命令:
lb-ng server/server.js client/js/services/lb-services.js
生成的 lb-services.js 就是你在 Angular 里访问 LoopBack REST API 的“桥”。
拷贝官方示例的前端代码
官方教程为了省事,直接让你从 intermediate 示例里把现成的前端拷过来:
git clone https://github.com/strongloop/loopback-getting-started-intermediate.git
cp -r loopback-getting-started-intermediate/client ./client
拷完之后,你的 client 目录大概长这样:
client/index.html:单页应用入口。client/css/style.css:样式。client/js/app.js:AngularJS 应用入口、路由配置。client/js/controllers/*.js:比如auth.js,处理登录/登出、切换“全部评论 / 我的评论”等逻辑。
index.html 会把 lb-services.js 引入进来,然后在控制器里通过 Review, CoffeeShop, Reviewer 这些 Angular 服务来访问后端。
这一步使用现成代码,把它跑起来,多看看请求 / 响应长什么样,对以后自己写前端很有帮助。
总结
如果你只打算用 LoopBack 写内部 API,上面的很多步骤其实已经够你做一堆项目了。如果你还想深入,可以继续:
- Tutorials & Examples 里的一堆示例仓库(
loopback-example-access-control、loopback-example-app-logic等)。 - 关于 “Controlling data access”、“Creating model relations”、“Remote methods / Remote hooks” 这些专题文档,里面有更完整的细节。
参考资料
- LoopBack Documentation: Getting started - part II
- LoopBack Documentation: Define model relations
- LoopBack Documentation: Define access controls
- LoopBack Documentation: Introducing the Coffee Shop Reviews ap
- LoopBack Documentation: Create new models
- LoopBack Documentation: Define a remote hook
- LoopBack Documentation: Create AngularJS client
- LoopBack Documentation: Tutorials and examples