Meteor: właściwe użycie meteoru.wrapasync na serwerze
Tło
Próbuję zintegrować płatności stripe z moją stroną. Muszę utworzyć użytkownika stripe używając mojego prywatnego klucza stripe. Przechowuję ten klucz na moim serwerze i wywołuję metodę serwera, aby utworzyć użytkownika. Może jest inny sposób na osiągnięcie tego? Oto API stripe (skopiowane poniżej dla wygody): https://stripe.com/docs/api/node#create_customer//stripe api call
var Stripe = StripeAPI('my_secret_key');
Stripe.customers.create({
description: 'Customer for [email protected]',
card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
// asynchronously called
});
Moje próby i wyniki
Używam tego samego klienta kod z innym kodem serwera. Wszystkie próby natychmiast dają undefined na konsoli klienta.log(...), ale dać właściwą odpowiedź na konsoli serwera.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
}));
}
});
Próbowałem też obu bez Meteor.wrapAsync.
EDIT-ja również korzystam z tego pakietu: https://atmospherejs.com/mrgalaxy/stripe
4 answers
Z Meteor.wrapAsync
http://docs.meteor.com/#meteor_wrapasync widzisz, że musisz przekazać mu funkcję i opcjonalnie kontekst, podczas gdy podczas dwóch prób przekazujesz wynik wywołania asynchronicznej wersji 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
przekształca funkcję asynchroniczną w wygodną funkcję synchroniczną, która umożliwia pisanie sekwencyjnie wyglądającego kodu. (underhood wszystko jest nadal wykonywane w obrębie węzła asynchronicznego.js event loop).
We trzeba przejść do Meteor.wrapAsync
naszej funkcji API (Stripe.customers.create
) wraz z kontekstem funkcji, czyli this
wewnątrz ciała func API, który w tym przypadku jest Stripe.customers
.
EDIT:
Jak odzyskać błędy ?
Tradycyjne funkcje API w stylu węzłów zwykle przyjmują wywołanie zwrotne jako ostatni argument, który zostanie wywołany ostatecznie po zakończeniu wymaganego zadania. To wywołanie zwrotne pobiera 2 argumenty: błąd i dane, albo jeden będzie null zgodnie z wynikiem sprawdzam.
Jak uzyskać dostęp do obiektu błędu przy użyciu funkcji zawiniętych synchronicznie zwracanych przez Meteor.wrapAsync
?
Musimy polegać na użyciu bloków try/catch, ponieważ w przypadku błędu zostanie on wyrzucony przez funkcję sync zamiast być przekazywany jako pierwszy argument funkcji asynchronicznej.
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);
});
Dlaczego Stripe nie musi być przekazywane?
JavaScript nie ma pojęcia "przestrzeni nazw" , więc deweloperzy API używają wspólnej sztuczki definiowania globalnego obiekt pełniący funkcję przestrzeni nazw API, właściwości zdefiniowane w tym obiekcie są "podmodułami" API.
Oznacza to, że Stripe.customers
jest podmodułem interfejsu API Stripe, eksponującym funkcje związane z klientami, i jako takie te funkcje this
kontekstem jest Stripe.customers
, a nie Stripe
.
Możesz go przetestować samodzielnie, kopiując ten szyderczy kod w konsoli przeglądarki:
Stripe={
customers:{
create:function(){
console.log(this==Stripe.customers);
}
}
};
A następnie wywołanie funkcji stub w konsoli przeglądarki w następujący sposób:
> Stripe.customers.create();
true
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-20 11:50:25
Inną opcją jest pakiet , który osiąga podobne cele.
meteor add meteorhacks:async
Z pakietu README:
Async.wrap (function)
owinąć funkcję asynchroniczną i umożliwić jej uruchomienie wewnątrz Meteor bez wywołania zwrotnego.
//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;
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 03:52:20
Po pierwsze, dzięki @saimeunt za odpowiedź, która wyjaśnia niektóre trudne pojęcia. Jednak miałem problem z chce klasyczny asynchroniczny callback (err, wynik) pokazując zarówno błąd, jak i wynik z powrotem na kliencie, tak, że mogę dać informacyjne wiadomości w przeglądarce.
Rozwiązałem to w ten sposób:
Kod serwera:
var Stripe = StripeAPI(STRIPE_SECRET_KEY);
Meteor.methods({
createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});
Kod klienta:
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);
});
Wygląda schludnie. Jednak niestety wrapAsync ma otwarty błąd, (zobacz https://github.com/meteor/meteor/issues/2774 ), ponieważ nie przywraca WŁAŚCIWEGO błędu do wywołującego. Geniusz zwany Faceyspacey napisał zamiennik o nazwie Meteor.makeAsync (), którą znajdziesz na stronie błędu, o której wspomniałem, która jednak zwraca wynik lub błąd do zmiennej 'result', pozostawiając zmienną' error ' niezdefiniowaną.
Na razie mi to nie przeszkadza, przynajmniej Mam Haka na właściwy obiekt błędu.
Jeśli używasz makeAsync (), musisz Importuj Futures tak:
Meteor.startup(function () {
//this is so that our makeAsync function works
Future = Npm.require('fibers/future');
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-19 15:21:48
Ponieważ będziesz potrzebował prawie każdej funkcji do owinięcia w Asynchronizację, powinieneś użyć tego pakietu https://atmospherejs.com/copleykj/stripe-sync
, który prewrapuje wszystkie funkcje stripe za pomocą WrapAsync, co ułatwi Ci życie i oczyści kod.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-10 20:22:06