【问题标题】:Meteor: Access Functions from Client, Run on ServerMeteor:从客户端访问功能,在服务器上运行
【发布时间】:2016-07-14 10:56:49
【问题描述】:

我正在尝试创建一个使用 Steam API 的程序。我希望能够调用该方法以从客户端检索用户的信息,同时保持该方法的实际代码对客户端保密,因为它包含一个 API 密钥。我尝试在服务器文件夹中将方法定义为全局,如下所示:

key = 'xxxxxxxxxxxxxxxx';

Meteor.steamFunctions = {
    getName: function(user){
        var userSteamId = user.profile.id;
        Meteor.http.get('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=' + key + '&steamids=' + userSteamId, function(error, resultJSON){
            if (error){
                return 'Error in Steam API';
            } else {
                var json = JSON.parse(resultJSON);
                return json.personaname;
            }
        })
    },

    getPic: function(user){
        var userSteamId = user.profile.id;
        Meteor.http.get('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=' + key + '&steamids=' + userSteamId, function(error, resultJSON){
            if (error){
                return 'Error in Steam API';
            } else {
                var json = JSON.parse(resultJSON);
                return json.avatarfull;
            }
        })
    }
}

然后我尝试在客户端脚本中这样调用它:

if (Meteor.isClient){
    Template.profile.helpers({
        'getName': function(){
            return Meteor.steamFunctions.getName(Meteor.user());
        }
    });
}

然而,这会抛出

Exception in template helper: TypeError: Cannot read property 'getName' of undefined
    at Object.Template.profile.helpers.getName

我怎样才能在访问数据的同时对用户保密?

【问题讨论】:

    标签: meteor methods public


    【解决方案1】:

    嗯,这并不像在Meteor 全局变量中添加一个属性那么简单。此外,执行此操作的远程方法/调用 API 将涉及异步代码。

    使用 API 密钥在服务器端以仅在服务器上可见的代码形式调用 API,例如./server 子目录。在服务器端定义一个Meteor.method,可以在客户端用Meteor.call 调用。

    在服务器端 Meteor 方法中有 method security checks 可以用来检查登录用户或用户 ID,并使用它来决定是拨打电话还是忽略请求。如果请求不正确或出现错误,您可以从服务器端抛出一个新的 Meteor.Error,但这些会占用资源进行通信。

    要了解 Meteor 的一点是,改变 Javascript 在浏览器或服务器上的行为方式并没有什么神奇之处。服务器最终运行的是 nodejs。在服务器上定义的对象不会神奇地迁移到客户端,反之亦然。如果在两者上都定义了一个对象,则它实际上是两段单独的代码。

    因此,在客户端代码中,Meteor.call 从浏览器调用服务器端代码...实际上是在使用现有的异步 websocket 或 ajax API。这意味着您将需要构造客户端代码以在浏览器上提供回调函数来处理查找NamePic 的异步返回结果。直接返回和命令式编码风格是不可能的。

    通常,您会希望根据查找返回的信息更新用户屏幕上的某些内容。通常的 Meteor 编码是让回调函数用Session.set() 更新session global variable。模板可以引用这些会话变量,并且通过隐含或显式的 Tracker.autorun(),可以在 API 返回数据时更新屏幕。

    【讨论】:

      【解决方案2】:

      你需要:

      1. 将您的steamFunctions 移动到仅在服务器上定义的methods
      2. 从客户端正确调用方法。

      以下是基于您的原始问题的一些示例代码。请注意,这尚未经过测试,可能需要进行一些调整。

      服务器/methods.js

      const KEY = 'xxxxxxxxxxxxxxxx';
      const URL = 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002';
      
      Meteor.methods({
        getName() {
          const userSteamId = Meteor.user().profile.id;
          const params = {
            key: KEY,
            steamids: userSteamId,
          };
      
          try {
            var result = HTTP.get(URL, { params });
      
            // Double check this - I have no idea what this API returns. The value
            // you want may be nested under result, like result.data or something.
            return JSON.parse(result).personaname;
          } catch (e) {
            // Something bad happened - maybe throw an error.
            return false;
          }
        },
      });
      

      请注意,此方法是在服务器上定义的,因此我们不会将 KEY 暴露给客户端。另请注意,我们使用的是同步版本的HTTP api,因此可以将值返回给客户端。

      client/lib/user.js

      Tracker.autorun(function () {
        user = Meteor.user();
        if (user && user.profile && user.profile.id) {
          Meteor.call('getName', (err, name) => {
            Session.set('steamName', name);
          });
        } else {
          Session.set('steamName', '');
        }
      });
      

      当用户日志更新或更新时,获取蒸汽名称并设置全局会话变量。

      client/templates/profile.js

      Template.profile.helpers({
        getName: function () {
          return Session.get('steamName');
        },
      });
      

      阅读steamName 会话变量以在您的模板中使用。

      【讨论】:

      • 感谢您的回复...我现在的麻烦是在用户实际“登录”之前从客户端调用该方法,从而给我一个“无法从未定义访问”错误..我尝试在 Iron-router 路由器文件中使用 waitOn Meteor.user(),我也尝试安装 fast-render ......似乎都不起作用。您知道完成等待的更简单方法吗?不管怎样,谢谢你的回答,我真的很感激:)
      • 是的,这应该很容易解决。在我添加它之前,你能告诉我蒸汽名称何时更改以及配置文件模板何时呈现?例如,如果名称从不更改并且配置文件被大量渲染,您将进行大量不必要的调用。在这种情况下,它可能应该全局设置。
      • 所以...我走了另一条路,我不确定它是否可行/正确。我正在考虑创建一个包含所有信息的集合,并将信息插入 onCreateUser(),并在每次使用 onLogin() 登录时调用和更新信息……然后让他们通过集合。但是,我不确定这是否是完成它的最佳方法。你对此有什么看法吗?抱歉,我有点带你去兜风......我只是不知道做事的最佳方式:)
      • 有很多方法可以解决这个问题。您的建议听起来不错,但您可能只想直接写入用户的文档而不是使用另一个集合 - 我想这取决于需要多少数据和需要什么数据。我用一个使用全局自动运行和会话变量的示例更新了答案。这是一个简单的解决方案,应该在适当的时候更新。如果您的应用程序不断更新用户的文档,我唯一不会使用它。您可以随时在getName 中输入控制台日志,以查看调用频率是否存在问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-04
      • 2018-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      相关资源
      最近更新 更多