Node.js學(xué)習(xí):使用Loopback3.0構(gòu)建應(yīng)用程序(二)

一、咖啡店點(diǎn)評(píng)應(yīng)用

應(yīng)用概述

咖啡店點(diǎn)評(píng)是一個(gè)網(wǎng)站,您可以用來(lái)發(fā)布咖啡店的評(píng)論。

這個(gè)應(yīng)用程序用到了兩個(gè)不同的數(shù)據(jù)源:它會(huì)將評(píng)論者數(shù)據(jù)存儲(chǔ)在MySQL數(shù)據(jù)庫(kù)中,并把咖啡店和評(píng)論數(shù)據(jù)存儲(chǔ)在MongoDB數(shù)據(jù)庫(kù)中。

這個(gè)應(yīng)用有三個(gè)數(shù)據(jù)模型:

  • CoffeeShop(這個(gè)模型我們已經(jīng)在上一步中定義好了)
  • Review
  • Reviewer

它們有如下關(guān)系:

  • 一個(gè)CoffeeShop擁有多個(gè)review
  • 一個(gè)CoffeeShop擁有多個(gè)reviewer
  • 一個(gè)review屬于一個(gè)CoffeeShop
  • 一個(gè)review屬于一個(gè)reviewer
  • 一個(gè)reviewer擁有多個(gè)review

一般來(lái)說(shuō),用戶可以創(chuàng)建,編輯,刪除和閱讀咖啡店的評(píng)論,并通過(guò)ACLs指定基本規(guī)則和權(quán)限:

  • 任何人都可以閱讀評(píng)論,但必須先登錄才能創(chuàng)建,編輯或刪除它們。
  • 任何人都可以注冊(cè)為用戶,然后能夠登錄或者注銷。
  • 登錄用戶可以創(chuàng)建新的評(píng)論,編輯或刪除自己的評(píng)論,但是他們不能修改一開(kāi)始選擇的咖啡店。

二、創(chuàng)建新的數(shù)據(jù)源

添加一個(gè)新的數(shù)據(jù)源

除了將API連接到上一步創(chuàng)建的MySQL數(shù)據(jù)源之外,現(xiàn)在還需要添加一個(gè)MongoDB數(shù)據(jù)源。

lb datasource

出現(xiàn)提示時(shí),回復(fù)如下:

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

接下來(lái)輸入一些數(shù)據(jù)源設(shè)置,如主機(jī),端口,用戶,密碼和數(shù)據(jù)庫(kù)名稱,然后安裝數(shù)據(jù)庫(kù)連接器。

? 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

數(shù)據(jù)庫(kù)連接器可以使用npm自行安裝,數(shù)據(jù)源設(shè)置也可以手動(dòng)添加到server/ datasources.json中。

安裝MongoDB連接器:

npm install --save loopback-connector-mongodb

配置數(shù)據(jù)源

server/datasources.json中配置新的數(shù)據(jù)源。

server/datasources.json

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

三、添加新的數(shù)據(jù)模型

定義Review數(shù)據(jù)模型

輸入:

lb model

出現(xiàn)提示時(shí),輸入或選擇以下內(nèi)容:

  • 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.

然后根據(jù)提示加入以下屬性。

<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數(shù)據(jù)模型

輸入:

lb model

出現(xiàn)提示時(shí),輸入或選擇以下內(nèi)容:

  • Model name: Reviewer
  • Data source: mongoDs (mongodb)
  • Base class: 選擇User.
  • Expose Reviewer via the REST API?: 選擇默認(rèn)選項(xiàng),yes
  • Custom plural form (used to build REST URL): 選擇默認(rèn)選項(xiàng),yes

接下來(lái)不需要給Reviewer添加任何屬性,它們都是從基本的用戶模型繼承下來(lái)的。

更新啟動(dòng)腳本,添加一些原始數(shù)據(jù)

在啟動(dòng)腳本server/boot/create-sample-models.js中添加一些代碼,這個(gè)啟動(dòng)腳本有如下幾個(gè)功能:

  • createCoffeeShops()為CoffeeShop模型創(chuàng)建一個(gè)MySQL表,并將數(shù)據(jù)添加到表中。
  • createReviewers()使用自動(dòng)遷移在MongoDB中創(chuàng)建Reviewer數(shù)據(jù)結(jié)構(gòu),并向其添加數(shù)據(jù)。
  • createReviews()使用自動(dòng)遷移在MongoDB中創(chuàng)建評(píng)論數(shù)據(jù)結(jié)構(gòu),并向其添加數(shù)據(jù)。

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);
    });
  }
};

四、定義模型的關(guān)系

介紹

LoopBack支持許多不同類型的模型關(guān)系:BelongsTo, HasMany, HasManyThrough, and HasAndBelongsToMany等等。

在“咖啡店評(píng)論”應(yīng)用程序中,有以下幾種關(guān)系:

  • 一個(gè)CoffeeShop擁有多個(gè)review
  • 一個(gè)CoffeeShop擁有多個(gè)reviewer
  • 一個(gè)review屬于一個(gè)CoffeeShop
  • 一個(gè)review屬于一個(gè)reviewer
  • 一個(gè)reviewer擁有多個(gè)review

定義關(guān)系

現(xiàn)在,我們將使用lb relation來(lái)定義這些模型之間的關(guān)系。

一個(gè)CoffeeShop擁有多個(gè)review,沒(méi)有中間模型和外鍵。

? 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

一個(gè)CoffeeShop擁有多個(gè)reviewer,沒(méi)有中間模型和外鍵

? 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

一個(gè)review屬于一個(gè)CoffeeShop,沒(méi)有外鍵。

? 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

一個(gè)review屬于一個(gè)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

一個(gè)reviewer擁有多個(gè)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模型文件

現(xiàn)在,查看common/models/review.json。你應(yīng)該會(huì)看到這些:

common/models/review.json

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

同樣,其他的json文件中應(yīng)該有如下代碼:

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": ""
  }
},
...

五、定義權(quán)限控制

權(quán)限控制簡(jiǎn)介

loopback應(yīng)用通過(guò)模型訪問(wèn)數(shù)據(jù),因此控制對(duì)數(shù)據(jù)的訪問(wèn)意味著對(duì)模型進(jìn)行權(quán)限的控制:也就是說(shuō),指定什么角色可以在模型上執(zhí)行讀取和寫(xiě)入數(shù)據(jù)的方法。loopback權(quán)限控制由權(quán)限控制列表或ACL決定。

接下來(lái),我們將為Review模型設(shè)置權(quán)限控制。

權(quán)限控制應(yīng)執(zhí)行以下規(guī)則:

  • 任何人都可以閱讀評(píng)論。但是創(chuàng)建、編輯和刪除的操作必須在登錄之后才有權(quán)限。
  • 任何人都可以注冊(cè)為用戶,可以登錄和登出。
  • 登錄用戶可以創(chuàng)建新的評(píng)論,編輯或刪除自己的評(píng)論。然而,他們不能修改咖啡店的評(píng)論。

定義權(quán)限控制

這次我們使用lb的acl子命令。

$ lb acl

首先,拒絕所有人操作所有接口,這通常是定義ACL的起點(diǎn),因?yàn)槟梢赃x擇性地允許特定操作的訪問(wèn)。

? 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

現(xiàn)在允許所有人對(duì)reviews進(jìn)行讀操作

? 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

允許通過(guò)身份驗(yàn)證的用戶對(duì)coffeeshops進(jìn)行讀操作,也就是說(shuō),已登錄的用戶可以瀏覽所有咖啡店。

? 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

允許經(jīng)過(guò)身份驗(yàn)證的用戶對(duì)reviews進(jìn)行寫(xiě)操作,也就是說(shuō),已登錄的用戶可以添加一條評(píng)論。

? 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的作者有權(quán)限(其“所有者”)對(duì)其進(jìn)行任何更改。

$ 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文件

完成上述步驟,此時(shí)的common/models/review.json中的ACL部分應(yīng)如下所示:

六、定義一個(gè)遠(yuǎn)程鉤子

遠(yuǎn)程鉤子介紹

遠(yuǎn)程鉤子(remote hook)是一個(gè)在遠(yuǎn)程方法(自定義遠(yuǎn)程方法或內(nèi)置CRUD方法)之前或之后執(zhí)行的功能。

在這個(gè)例子中,我們將定義一個(gè)遠(yuǎn)程鉤子,每當(dāng)在Review模型上調(diào)用create()方法時(shí)(在創(chuàng)建新的評(píng)論時(shí)),它將被調(diào)用。

您可以定義兩種遠(yuǎn)程鉤子:

  • beforeRemote()在遠(yuǎn)程方法之前運(yùn)行。
  • afterRemote()在遠(yuǎn)程方法之后運(yùn)行。

在這兩種情況下,有兩個(gè)參數(shù)可以供我們使用:一個(gè)與要鉤子函數(shù)的遠(yuǎn)程方法匹配的字符串,和一個(gè)回調(diào)函數(shù)。

創(chuàng)建一個(gè)遠(yuǎn)程鉤子

這里,您將在review模型中定義一個(gè)遠(yuǎn)程鉤子,具體來(lái)說(shuō)是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();
  });
};

在創(chuàng)建Review模型的新實(shí)例之前調(diào)用此函數(shù)。The code:

  • 設(shè)置publisherId為請(qǐng)求中的userId
  • 設(shè)置日期為當(dāng)前日期。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容