8:方法
在此步骤之前,任何用户都可以通过直接在客户端进行更改来编辑数据库的任何部分。这对于快速原型设计很有用,但实际应用需要控制数据访问。
在 Meteor 中,安全地进行服务器更改的最简单方法是声明方法,而不是直接在客户端调用 insert、update 或 remove。
使用方法,您可以验证用户是否已通过身份验证并有权执行特定操作,然后相应地更改数据库。
Meteor 方法是使用函数 Meteor.call 与服务器通信的一种方式。您需要提供方法名称和参数。
您可以在此处阅读有关方法的更多信息。
8.1:禁用快速原型设计
每个新创建的 Meteor 项目默认都安装了 insecure
包。
此包允许我们从客户端编辑数据库,这对于快速原型设计很有用。
我们需要将其删除,因为顾名思义,它是 insecure
(不安全的)。
meteor remove insecure
现在您的应用更改将不再起作用,因为您已撤销所有客户端数据库权限。如果您尝试插入一个新任务,您将在浏览器控制台中看到 insert failed: Access denied
。
8.2:添加任务方法
现在您需要定义方法。
您需要为我们想要在客户端执行的每个数据库操作定义一个方法。
方法应在客户端和服务器上执行的代码中定义,以支持乐观 UI。
乐观 UI
当我们使用 Meteor.call
在客户端调用方法时,会发生两件事
- 客户端向服务器发送请求以在安全环境中运行该方法。
- 方法的模拟直接在客户端运行,试图预测调用的结果。
这意味着新创建的任务实际上会在服务器返回结果之前出现在屏幕上。
如果结果与服务器匹配,则一切照旧。否则,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:实现方法调用
由于您已定义了方法,因此您需要更新我们操作集合以使用它们的位置。
在 App.js
文件中,您应该调用 Meteor.call('tasks.insert', text);
而不是 TasksCollection.insert
。
imports/ui/App.js
...
Template.form.events({
"submit .task-form"(event) {
// Prevent default browser form submit
event.preventDefault();
// Get value from form element
const target = event.target;
const text = target.text.value;
// Insert a task into the collection
Meteor.call('tasks.insert', text);
// Clear form
target.text.value = '';
}
})
在 Task.js
文件中,您应该调用 Meteor.call('tasks.setIsChecked', _id, !isChecked);
而不是 TasksCollection.update
,以及 Meteor.call('tasks.remove', _id)
而不是 TasksCollection.remove
。请记住还要删除 TasksCollection
导入。
imports/ui/Task.js
import { Template } from 'meteor/templating';
import './Task.html';
Template.task.events({
'click .toggle-checked'() {
Meteor.call('tasks.setIsChecked', this._id, !this.isChecked);
},
'click .delete'() {
Meteor.call('tasks.remove', this._id);
},
});
现在您的输入和按钮将再次开始工作。那么您学到了什么呢?
- 当我们将任务插入数据库时,我们可以安全地验证用户是否已通过身份验证;
createdAt
字段是否正确;userId
是否合法。 - 我们以后可以向方法中添加额外的验证逻辑。
- 我们的客户端代码与我们的数据库逻辑更加隔离。我们不再在事件处理程序中执行大量操作,而是拥有可从任何地方调用的方法。
8.4:api 和 db 文件夹
让我们花点时间思考一下。集合文件位于 api
文件夹中,但在您的项目中,API 表示服务器和客户端之间的通信层。但是,集合不再执行此角色。因此,您应该将 TasksCollection
文件移动到一个名为 db
的新文件夹中。
此更改不是必需的,但我们建议您这样做以保持名称一致。
请记住修复您的导入,您在以下文件中对 TasksCollection
有 3 个导入
imports/api/tasksMethods.js
imports/ui/App.js
server/main.js
它们应该从 import { TasksCollection } from '/imports/api/TasksCollection';
更改为 import { TasksCollection } from '/imports/db/TasksCollection';
。
由于我们在此步骤中没有更改对用户可见的任何内容,因此您的应用应该看起来与之前完全相同。您可以使用Meteor DevTools查看发送到服务器的消息和返回的结果。此信息在 DDP
选项卡中提供。
DDP 是 Meteor 通信层背后的协议。您可以在此处了解更多信息。
我们建议您将错误类型的 check
调用更改为产生一些错误,然后您也可以了解在这些情况下会发生什么。
回顾:查看您的代码应如何显示此处。
在下一步中,我们将开始使用发布在每种情况下发布必要的数据。