8:方法

在此步骤之前,应用程序的任何用户都可以编辑数据库的任何部分,直接在客户端进行更改。这对于快速原型设计可能没问题,但真正的应用程序需要控制对其数据访问。

在 Meteor 中,安全地在服务器上进行更改最简单的方法是声明方法,而不是直接在客户端调用insertupdateremove

使用方法,您可以验证用户是否已通过身份验证并有权执行某些操作,然后相应地更改数据库。

Meteor 方法是一种使用函数Meteor.call与服务器通信的方式,您需要提供方法的名称和参数。

您可以在此处阅读更多关于方法的信息。

8.1:禁用快速原型设计

每个新创建的 Meteor 项目默认安装了insecure包。

如上所述,此包允许我们从客户端编辑数据库,这对于快速原型设计很有用。

我们需要将其删除,因为顾名思义,它是insecure(不安全的)。

meteor remove insecure

现在您的应用程序更改将不再起作用,因为您已撤销所有客户端数据库权限。例如,尝试插入一个新任务,您将在浏览器控制台中看到insert failed: Access denied

8.2:添加任务方法

现在您需要定义方法。

您需要为要在客户端上执行的每个数据库操作定义一个方法。

方法应在客户端和服务器上执行的代码中定义,以支持乐观 UI。

乐观 UI

当我们使用Meteor.call在客户端调用方法时,会同时发生两件事

  1. 客户端向服务器发送请求,以在安全的环境中运行该方法。
  2. 方法的模拟直接在客户端运行,试图预测调用的结果。

这意味着新创建的任务实际上会在服务器返回结果之前显示在屏幕上。

如果结果与服务器的结果匹配,则一切照旧,否则 UI 会被修补以反映服务器的实际状态。

Meteor 为您完成了所有这些工作,您无需担心,但了解正在发生的事情很重要。您可以在此处阅读更多关于乐观 UI 的信息。

现在,您应该在imports/api文件夹中添加一个名为tasksMethods的新文件。在此文件中,对于您在客户端执行的每个操作,接下来我们将从客户端调用这些方法,而不是直接使用 Mini Mongo 操作。

在方法中,您可以使用一些特殊属性,这些属性已准备好在this对象上使用,例如,您有经过身份验证的用户的userId

imports/api/tasksMethods.js

import { check } from 'meteor/check';
import { TasksCollection } from './TasksCollection';
 
Meteor.methods({
  'tasks.insert'(text) {
    check(text, String);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    TasksCollection.insert({
      text,
      createdAt: new Date,
      userId: this.userId,
    })
  },
 
  'tasks.remove'(taskId) {
    check(taskId, String);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    TasksCollection.remove(taskId);
  },
 
  'tasks.setIsChecked'(taskId, isChecked) {
    check(taskId, String);
    check(isChecked, Boolean);
 
    if (!this.userId) {
      throw new Meteor.Error('Not authorized.');
    }
 
    TasksCollection.update(taskId, {
      $set: {
        isChecked
      }
    });
  }
});

如您在代码中看到的,我们还使用了check包来确保我们接收到了预期的输入类型,这对于确保您准确地知道您在数据库中插入或更新的内容非常重要。

最后一步是确保您的服务器正在注册这些方法,您可以通过导入此文件(强制评估)在server/main.js中执行此操作。

server/main.js

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { TasksCollection } from '/imports/db/TasksCollection';
import './imports/api/tasksMethods';

请注意,您不需要从导入中获取任何符号,您只需要要求服务器导入该文件,然后Meteor.methods将被评估,并在服务器启动时注册您的方法。

8.3:实现方法调用

由于您已定义了方法,因此您需要更新我们操作集合以使用它们的位置。

TaskForm文件中,您应该调用Meteor.call('tasks.insert', text);而不是TasksCollection.insert。请记住也要修复您的导入。

imports/ui/components/TaskForm.vue

..
<script>
  import { Meteor } from 'meteor/meteor';
  ..
  methods: {
    handleSubmit(event) {
      if (this.newTask.length === 0) return;

      Meteor.call('tasks.insert', this.newTask.trim());

      // Clear form
      this.newTask = "";
    }
  },
}
</script>

请注意,您的TaskForm组件不再需要接收用户,因为我们在服务器中获取了userId

Task组件中,您应该调用Meteor.call('tasks.setIsChecked', _id, !isChecked);而不是TasksCollection.update,以及Meteor.call('tasks.remove', _id)而不是TasksCollection.remove

请记住还要从您的<TaskForm />中删除user属性。

imports/ui/components/Task.vue

..
methods: {
    toggleChecked() {
      // Set the checked property to the opposite of its current value
      Meteor.call('tasks.setIsChecked', this.task._id, !this.task.isChecked);
    },
    deleteThisTask() {
      Meteor.call('tasks.remove', this.task._id);
    }
}
..

现在您的输入和按钮将再次开始工作。您获得了什么?

  1. 当我们将任务插入数据库时,我们可以安全地验证用户是否已通过身份验证;createdAt字段是否正确;以及userId是否合法。
  2. 如果需要,我们以后可以在方法中添加额外的验证逻辑。
  3. 我们的客户端代码与我们的数据库逻辑更加隔离。我们不是在事件处理程序中执行大量操作,而是拥有可从任何地方调用的方法。

8.4:api 和 db 文件夹

我们想在这里花点时间思考一下,集合文件所在的文件夹是api,但项目中的 API 表示服务器和客户端之间的通信层,但集合不再执行此角色。因此,您应该将TasksCollection文件移动到一个名为db的新文件夹中。

此更改不是必需的,但建议保持我们的名称与实际情况一致。

请记住修复您的导入,您在以下文件中对TasksCollection有 3 个导入

  • imports/api/tasksMethods.js
  • imports/ui/components/TaskForm.vue
  • server/main.js

它们应该从import { TasksCollection } from '/imports/api/TasksCollection';更改为import { TasksCollection } from '/imports/db/TasksCollection';

您的应用程序应该看起来与之前完全一样,因为我们在此步骤中没有更改对用户可见的任何内容。您可以使用Meteor DevTools查看发送到服务器的消息和返回的结果,此信息在DDP选项卡中可用。

DDP 是 Meteor 通信层背后的协议,您可以在此处了解更多信息。

我们建议您将错误类型的check调用更改为生成一些错误,然后您也可以了解在这些情况下会发生什么。

回顾:您可以查看此步骤结束时代码应如何编写此处

在下一步中,我们将开始使用发布,只发布每种情况下必要的数据。

在 GitHub 上编辑
// 搜索框