【问题标题】:Meteor: Proper use of Meteor.wrapAsync on serverMeteor:在服务器上正确使用 Meteor.wrapAsync
【发布时间】:2023-03-03 07:36:21
【问题描述】:

背景

我正在尝试将条带支付功能集成到我的网站中。我需要使用我的私有条带密钥创建一个条带用户。我将此密钥存储在我的服务器上,并调用服务器方法来创建用户。也许还有另一种方法可以做到这一点?这是条带api(为方便起见,复制如下): https://stripe.com/docs/api/node#create_customer

//stripe api call
var Stripe = StripeAPI('my_secret_key');

Stripe.customers.create({
  description: 'Customer for test@example.com',
  card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
  // asynchronously called
});

我的尝试和结果

我一直在使用相同的客户端代码和不同的服务器代码。所有尝试立即在客户端的 console.log(...) 上给出 undefined 但在服务器 console.log(...) 上给出正确的响应:

//client
Meteor.call('stripeCreateUser', options, function(err, result) {
  console.log(err, result);
});

//server attempt 1
var Stripe = StripeAPI('my_secret_key');

Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }, function (err, res) {
            console.log(res, err);
            return (res || err);
        }));
    }
});

//server attempt 2
var Stripe = StripeAPI('my_secret_key');

Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }));
    }
});

我也尝试过不使用 Meteor.wrapAsync。

编辑 - 我也在使用这个包: https://atmospherejs.com/mrgalaxy/stripe

【问题讨论】:

  • 哦,嘿,用谷歌搜索了这个问题,它几乎与我的代码相同

标签: javascript meteor stripe-payments


【解决方案1】:

Meteor.wrapAsynchttp://docs.meteor.com/#meteor_wrapasync 中,您可以看到您需要向它传递一个函数和一个可选的上下文,而在您的两次尝试中,您传递的是调用Stripe.customers.create 的异步版本的结果。

Meteor.methods({
  stripeCreateUser: function(options) {
    // get a sync version of our API async func
    var stripeCustomersCreateSync=Meteor.wrapAsync(Stripe.customers.create,Stripe.customers);
    // call the sync version of our API func with the parameters from the method call
    var result=stripeCustomersCreateSync({
      description: 'Woot! A new customer!',
      card: options.ccToken,
      plan: options.pricingPlan
    });
    // do whatever you want with the result
    console.log(result);
  }
});

Meteor.wrapAsync 将异步函数转换为方便的同步函数,允许编写顺序查找的代码。 (底层一切仍然在异步 Node.js 事件循环中执行)。

我们需要将我们的 API 函数 (Stripe.customers.create) 连同函数上下文一起传递给 Meteor.wrapAsync,即 API 函数主体内的 this,在本例中为 Stripe.customers

编辑:

如何找回错误?

传统的节点样式 API 函数通常将回调作为最后一个参数,当所需任务完成时最终会调用该回调。此回调有 2 个参数:error 和 data,根据调用的结果,任何一个都将为 null。

我们如何使用Meteor.wrapAsync返回的同步包装函数访问错误对象?

我们必须依赖使用 try/catch 块,因为如果发生错误,它将被同步函数抛出,而不是作为异步函数回调的第一个参数传递。

try{
  var result=syncFunction(params);
  console.log("result :",result);
}
catch(error){
  console.log("error",error);
}
// is the equivalent of :
asyncFunc(params,function(error,result){
  if(error){
    console.log("error",error);
    return;
  }
  console.log("result :",result);
});

为什么不需要通过 Stripe?

JavaScript 没有“命名空间”的概念,因此 API 开发人员使用定义一个全局对象作为 API 命名空间的常用技巧,在该对象上定义的属性是 API 的“子模块”。 这意味着 Stripe.customers 是 Stripe API 的子模块,暴露了与客户相关的函数,因此这些函数 this 的上下文是 Stripe.customers,而不是 Stripe

您可以通过在浏览器控制台中复制粘贴此模拟代码来自行测试:

Stripe={
  customers:{
    create:function(){
      console.log(this==Stripe.customers);
    }
  }
};

然后像这样在浏览器控制台中调用存根函数:

> Stripe.customers.create();
true

【讨论】:

  • 谢谢。我的文档不像你的回答那么清楚。我也不完全理解你的最后一段。我得再多研究一点。例如,为什么不需要通过 Stripe?再次感谢!
  • 如何得到错误?我在其他地方看到你仍然可以提供一个回调来接收(错误,数据),但在同步响应中,如果失败,会出现一个错误对象而不是数据吗?
  • 这在我的最新编辑中得到了解决,感谢您指出这一点。
  • @saimeunt 如果您从客户端使用 Meteor.call,如何将错误返回给客户端?如果您在客户端上设置回调(如果您不使用存根,则必须这样做),似乎无法返回传统样式(错误,结果),只能返回结果。我们将不胜感激地收到任何关于这方面的信息。
  • @saimeunt 我似乎已经解决了我发回错误的特殊问题。如果有人感兴趣,我会把它作为答案
【解决方案2】:

另一个选项是package,它实现了类似的目标。

meteor add meteorhacks:async

来自包自述文件:

Async.wrap(函数)

包装一个异步函数并允许它在 Meteor 中运行而无需回调。

//declare a simple async function
function delayedMessge(delay, message, callback) {
  setTimeout(function() {
    callback(null, message);
  }, delay);
}

//wrapping
var wrappedDelayedMessage = Async.wrap(delayedMessge);

//usage
Meteor.methods({
  'delayedEcho': function(message) {
    var response = wrappedDelayedMessage(500, message);
    return response;
  }
});

【讨论】:

  • 嗯,我想知道包装 Meteor.call 方法本身。 Meteor.call 从客户端调用时应该有一个回调。所以也许我们可以包装那个方法。
  • 是的,你可以这样做。请注意,Meteor 回调是异步的。见docs.meteor.com/#/full/meteor_call
  • 好的,所以对于同步方法调用不要给它回调
【解决方案3】:

首先,感谢@saimeunt 的回答,让一些困难的概念变得清晰。 但是我遇到了一个问题,就是想要一个经典的异步回调(错误,结果)在客户端上显示错误和结果,以便我可以在浏览器中提供信息性消息。

我是这样解决的:

服务器代码:

var Stripe = StripeAPI(STRIPE_SECRET_KEY);

Meteor.methods({
    createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});

客户端代码:

var stripeCallOptions = {
    description: 'Woot! A new customer!',
    card: ccToken,
    plan: pricingPlan
};


Meteor.call('createCust', stripeCallOptions, function(error, result){
    console.log('client error', error);
    console.log('client result', result);
});

看起来很整洁。然而不幸的是 wrapAsync 有一个开放的错误,(参见https://github.com/meteor/meteor/issues/2774),因为它不会为调用者恢复正确的错误。一个名为 Faceyspacey 的天才编写了一个名为 Meteor.makeAsync() 的替代方法,您可以在我提到的错误页面上找到它,但是它将结果或错误返回给“结果”变量,而“错误”变量未定义。 这对我来说很好,至少我有一个正确的错误对象的钩子。

如果你使用 makeAsync(),你需要像这样导入 Future:

Meteor.startup(function () {
    //this is so that our makeAsync function works
    Future = Npm.require('fibers/future');
});

【讨论】:

    【解决方案4】:

    由于您几乎需要将每个函数都包装在 Async 中,因此您应该使用这个包 https://atmospherejs.com/copleykj/stripe-sync 它使用 WrapAsync 预包装所有条带函数,让您的生活更轻松,代码更简洁。

    【讨论】:

    • 生活更轻松,代码更干净......你不应该认为他的生活很肮脏:p
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    相关资源
    最近更新 更多