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 { Meteor } from 'meteor/meteor';
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/TaskForm.svelte

<script>
    let newTask = '';

    const handleSubmit = () => {
        if (!newTask) return;

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

        // Clear form
        newTask = '';
    }
</script>
..

请注意,您的TaskForm组件不再需要接收用户,因为我们在服务器中获取了userId,因此您也可以删除export let user行。

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

请记住,当在<App />内部调用时,也要从您的<TaskForm />中删除user属性。

imports/ui/Task.svelte

<script>
    export let task;

    const toggleChecked = () =>
            Meteor.call('tasks.setIsChecked', task._id, !task.isChecked);


    const deleteThisTask = () => Meteor.call('tasks.remove', task._id);
</script>
..

imports/ui/App.svelte

..
<div class="app">
    ..
    <div class="main">
        {#if user}
            ..   
            <TaskForm />
..

8.4: api 和 db 文件夹

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

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

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

  • imports/api/tasksMethods.js
  • imports/ui/App.svelte
  • server/main.js

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

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

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

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

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

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

在 GitHub 上编辑
// 搜索框