Node.js学习:使用Loopback3.0构建应用程序(二)

一、咖啡店点评应用

应用概述

咖啡店点评是一个网站,您可以用来发布咖啡店的评论。

Node.js学习:使用Loopback3.0构建应用程序(二)_第1张图片

这个应用程序用到了两个不同的数据源:它会将评论者数据存储在MySQL数据库中,并把咖啡店和评论数据存储在MongoDB数据库中。

这个应用有三个数据模型:

  • CoffeeShop(这个模型我们已经在上一步中定义好了)
  • Review
  • Reviewer

它们有如下关系:

  • 一个CoffeeShop拥有多个review
  • 一个CoffeeShop拥有多个reviewer
  • 一个review属于一个CoffeeShop
  • 一个review属于一个reviewer
  • 一个reviewer拥有多个review

一般来说,用户可以创建,编辑,删除和阅读咖啡店的评论,并通过ACLs指定基本规则和权限:

  • 任何人都可以阅读评论,但必须先登录才能创建,编辑或删除它们。
  • 任何人都可以注册为用户,然后能够登录或者注销。
  • 登录用户可以创建新的评论,编辑或删除自己的评论,但是他们不能修改一开始选择的咖啡店。

二、创建新的数据源

添加一个新的数据源

除了将API连接到上一步创建的MySQL数据源之外,现在还需要添加一个MongoDB数据源。

lb datasource

出现提示时,回复如下:

? Enter the data-source name: mongoDs
? Select the connector for mongoDs: MongoDB (supported by StrongLoop)

接下来输入一些数据源设置,如主机,端口,用户,密码和数据库名称,然后安装数据库连接器。

? Enter the datasource name: mongodb
? Select the connector for mongodb: MongoDB (supported by StrongLoop)
? Connection String url to override other settings (eg: mongodb://username:password@hostname:port/database):
? host: localhost
? port: 27017
? user: demo
? password: ****
? database: demo
? Install loopback-connector-mongodb@^1.4 Yes

数据库连接器可以使用npm自行安装,数据源设置也可以手动添加到server/ datasources.json中。

安装MongoDB连接器:

npm install --save loopback-connector-mongodb

配置数据源

server/datasources.json中配置新的数据源。

server/datasources.json

...
"mongoDs": {
  "name": "mongoDs",
  "connector": "mongodb",
  "host": "demo.strongloop.com",
  "port": 27017,
  "database": "getting_started_intermediate",
  "username": "demo",
  "password": "L00pBack"
}

三、添加新的数据模型

定义Review数据模型

输入:

lb model

出现提示时,输入或选择以下内容:

  • Model name:Review
  • Data source: mongoDs (mongodb)
  • Base class: Use the down-arrow key to select PersistedModel.
  • Expose Reviewer via the REST API? Press RETURN to accept the default, Yes.
  • Custom plural form (used to build REST URL): Press RETURN to accept the default, Yes.
  • Common model or server only: Press RETURN to accept the default, common model.

然后根据提示加入以下属性。






















Property name Property type Required?
date date y
rating number n
comments string y

定义Reviewer数据模型

输入:

lb model

出现提示时,输入或选择以下内容:

  • Model name: Reviewer
  • Data source: mongoDs (mongodb)
  • Base class: 选择User.
  • Expose Reviewer via the REST API?: 选择默认选项,yes
  • Custom plural form (used to build REST URL): 选择默认选项,yes

接下来不需要给Reviewer添加任何属性,它们都是从基本的用户模型继承下来的。

更新启动脚本,添加一些原始数据

在启动脚本server/boot/create-sample-models.js中添加一些代码,这个启动脚本有如下几个功能:

  • createCoffeeShops()为CoffeeShop模型创建一个MySQL表,并将数据添加到表中。
  • createReviewers()使用自动迁移在MongoDB中创建Reviewer数据结构,并向其添加数据。
  • createReviews()使用自动迁移在MongoDB中创建评论数据结构,并向其添加数据。

server/boot/create-sample-models.js

var async = require('async');
module.exports = function(app) {
  //data sources
  var mongoDs = app.dataSources.mongoDs; // 'name' of your mongo connector, you can find it in datasource.json
  var mysqlDs = app.dataSources.mysqlDs;
  //create all models
  async.parallel({
    reviewers: async.apply(createReviewers),
    coffeeShops: async.apply(createCoffeeShops),
  }, function(err, results) {
    if (err) throw err;
    createReviews(results.reviewers, results.coffeeShops, function(err) {
      console.log('> models created sucessfully');
    });
  });
  //create reviewers
  function createReviewers(cb) {
    mongoDs.automigrate('Reviewer', function(err) {
      if (err) return cb(err);
      var Reviewer = app.models.Reviewer;
      Reviewer.create([{
        email: '[email protected]',
        password: 'foobar'
      }, {
        email: '[email protected]',
        password: 'johndoe'
      }, {
        email: '[email protected]',
        password: 'janedoe'
      }], cb);
    });
  }
  //create coffee shops
  function createCoffeeShops(cb) {
    mysqlDs.automigrate('CoffeeShop', function(err) {
      if (err) return cb(err);
      var CoffeeShop = app.models.CoffeeShop;
      CoffeeShop.create([{
        name: 'Bel Cafe',
        city: 'Vancouver'
      }, {
        name: 'Three Bees Coffee House',
        city: 'San Mateo'
      }, {
        name: 'Caffe Artigiano',
        city: 'Vancouver'
      }, ], cb);
    });
  }
  //create reviews
  function createReviews(reviewers, coffeeShops, cb) {
    mongoDs.automigrate('Review', function(err) {
      if (err) return cb(err);
      var Review = app.models.Review;
      var DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
      Review.create([{
        date: Date.now() - (DAY_IN_MILLISECONDS * 4),
        rating: 5,
        comments: 'A very good coffee shop.',
        publisherId: reviewers[0].id,
        coffeeShopId: coffeeShops[0].id,
      }, {
        date: Date.now() - (DAY_IN_MILLISECONDS * 3),
        rating: 5,
        comments: 'Quite pleasant.',
        publisherId: reviewers[1].id,
        coffeeShopId: coffeeShops[0].id,
      }, {
        date: Date.now() - (DAY_IN_MILLISECONDS * 2),
        rating: 4,
        comments: 'It was ok.',
        publisherId: reviewers[1].id,
        coffeeShopId: coffeeShops[1].id,
      }, {
        date: Date.now() - (DAY_IN_MILLISECONDS),
        rating: 4,
        comments: 'I go here everyday.',
        publisherId: reviewers[2].id,
        coffeeShopId: coffeeShops[2].id,
      }], cb);
    });
  }
};

四、定义模型的关系

介绍

LoopBack支持许多不同类型的模型关系:BelongsTo, HasMany, HasManyThrough, and HasAndBelongsToMany等等。

在“咖啡店评论”应用程序中,有以下几种关系:

  • 一个CoffeeShop拥有多个review
  • 一个CoffeeShop拥有多个reviewer
  • 一个review属于一个CoffeeShop
  • 一个review属于一个reviewer
  • 一个reviewer拥有多个review

定义关系

现在,我们将使用lb relation来定义这些模型之间的关系。

一个CoffeeShop拥有多个review,没有中间模型和外键。

? Select the model to create the relationship from: CoffeeShop
? Relation type: has many
? Choose a model to create a relationship with: Review
? Enter the property name for the relation: reviews
? Optionally enter a custom foreign key:
? Require a through model? No

一个CoffeeShop拥有多个reviewer,没有中间模型和外键

? Select the model to create the relationship from: CoffeeShop
? Relation type: has many
? Choose a model to create a relationship with: Reviewer
? Enter the property name for the relation: reviewers
? Optionally enter a custom foreign key:
? Require a through model? No

一个review属于一个CoffeeShop,没有外键。

? Select the model to create the relationship from: CoffeeShop
? Relation type: has many
? Choose a model to create a relationship with: Reviewer
? Enter the property name for the relation: reviewers
? Optionally enter a custom foreign key:
? Require a through model? No

一个review属于一个reviewer,外键是publisherId

? Select the model to create the relationship from: Review
? Relation type: belongs to
? Choose a model to create a relationship with: Reviewer
? Enter the property name for the relation: reviewer
? Optionally enter a custom foreign key: publisherId

一个reviewer拥有多个review,外键是publisherId

? Select the model to create the relationship from: Reviewer
? Relation type: has many
? Choose a model to create a relationship with: Review
? Enter the property name for the relation: reviews
? Optionally enter a custom foreign key: publisherId
? Require a through model? No

查看JSON模型文件

现在,查看common/models/review.json。你应该会看到这些:

common/models/review.json

...
"relations": {
  "coffeeShop": {
    "type": "belongsTo",
    "model": "CoffeeShop",
    "foreignKey": ""
  },
  "reviewer": {
    "type": "belongsTo",
    "model": "Reviewer",
    "foreignKey": "publisherId"
  }
},
...

同样,其他的json文件中应该有如下代码:

common/models/reviewer.json

...
"relations": {
  "reviews": {
    "type": "hasMany",
    "model": "Review",
    "foreignKey": "publisherId"
  }
},
...

common/models/coffee-shop.json

...
"relations": {
  "reviews": {
    "type": "hasMany",
    "model": "Review",
    "foreignKey": ""
  },
  "reviewers": {
    "type": "hasMany",
    "model": "Reviewer",
    "foreignKey": ""
  }
},
...

五、定义权限控制

权限控制简介

loopback应用通过模型访问数据,因此控制对数据的访问意味着对模型进行权限的控制:也就是说,指定什么角色可以在模型上执行读取和写入数据的方法。loopback权限控制由权限控制列表或ACL决定。

接下来,我们将为Review模型设置权限控制。

权限控制应执行以下规则:

  • 任何人都可以阅读评论。但是创建、编辑和删除的操作必须在登录之后才有权限。
  • 任何人都可以注册为用户,可以登录和登出。
  • 登录用户可以创建新的评论,编辑或删除自己的评论。然而,他们不能修改咖啡店的评论。

定义权限控制

这次我们使用lb的acl子命令。

$ lb acl

首先,拒绝所有人操作所有接口,这通常是定义ACL的起点,因为您可以选择性地允许特定操作的访问。

? Select the model to apply the ACL entry to: (all existing models)
? 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

现在允许所有人对reviews进行读操作

? Select the model to apply the ACL entry to: Review
? 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

允许通过身份验证的用户对coffeeshops进行读操作,也就是说,已登录的用户可以浏览所有咖啡店。

? Select the model to apply the ACL entry to: CoffeeShop
? Select the ACL scope: All methods and properties
? Select the access type: Read
? Select the role: Any authenticated user
? Select the permission to apply: Explicitly grant access

允许经过身份验证的用户对reviews进行写操作,也就是说,已登录的用户可以添加一条评论。

? Select the model to apply the ACL entry to: Review
? 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

使review的作者有权限(其“所有者”)对其进行任何更改

$ lb acl
? Select the model to apply the ACL entry to: Review
? 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

查看review.json文件

完成上述步骤,此时的common/models/review.json中的ACL部分应如下所示:

六、定义一个远程钩子

远程钩子介绍

远程钩子(remote hook)是一个在远程方法(自定义远程方法或内置CRUD方法)之前或之后执行的功能。

在这个例子中,我们将定义一个远程钩子,每当在Review模型上调用create()方法时(在创建新的评论时),它将被调用。

您可以定义两种远程钩子:

  • beforeRemote()在远程方法之前运行。
  • afterRemote()在远程方法之后运行。

在这两种情况下,有两个参数可以供我们使用:一个与要钩子函数的远程方法匹配的字符串,和一个回调函数。

创建一个远程钩子

这里,您将在review模型中定义一个远程钩子,具体来说是Review.beforeRemote

修改common/models/review.js,并添加以下代码:

common/models/review.js

module.exports = function(Review) {
  Review.beforeRemote('create', function(context, user, next) {
    context.args.data.date = Date.now();
    context.args.data.publisherId = context.req.accessToken.userId;
    next();
  });
};

在创建Review模型的新实例之前调用此函数。The code:

  • 设置publisherId为请求中的userId
  • 设置日期为当前日期。

你可能感兴趣的:(Node.js学习:使用Loopback3.0构建应用程序(二))