美文网首页LoopBack
Node.js学习:使用Loopback3.0构建应用程序(二)

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

作者: 断桥百晓生 | 来源:发表于2017-06-05 16:37 被阅读179次

    一、咖啡店点评应用

    应用概述

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

    这个应用程序用到了两个不同的数据源:它会将评论者数据存储在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.

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

    <table>
    <tr>
    <th>Property name</th>
    <th>Property type</th>
    <th>Required?</th>
    </tr>
    <tr>
    <td> date </td>
    <td> date </td>
    <td> y </td>
    </tr>
    <tr>
    <td> rating </td>
    <td> number </td>
    <td> n </td>
    </tr>
    <tr>
    <td> comments </td>
    <td> string </td>
    <td> y </td>
    </tr>
    </table>

    定义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: 'foo@bar.com',
            password: 'foobar'
          }, {
            email: 'john@doe.com',
            password: 'johndoe'
          }, {
            email: 'jane@doe.com',
            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构建应用程序(二)

      本文链接:https://www.haomeiwen.com/subject/hszifxtx.html