someMethod = new ValidatedMethod({
name: 'someMethodName',
validate: new SimpleSchema({
subId: {type: String, min:1},
planId: {type: String}
}).validator(),
async run(params){
try{
//params is undefined
}
}
});
Using async run(params) causes params to be undefined (seems like the context switches to Global context). Removing the async works fine (except that I cannot use await in the method body anymore obviously).
Why is this, and how can I still use await inside a ValidatedMethod?
Note1 : I call the method from the client like so -- and get the same result if I try to use a regular Meteor.methods({}) definition. I am calling the method using Meteor.apply from the client
ClientHelpers.callWithPromise = function(methodName, methodArgs){
//methodArgs must be an array
return new Promise(function(resolve, reject){
Meteor.apply(methodName, methodArgs, {wait:true}, function(error, result){
if (error){
reject(error);
}
console.log(result);
resolve(result);
});
});
}
Then, calling on client (am sure paramsObject is correct):
var myResult = await ClientHelpers.callWithPromise('someMethodName', [paramsObject]);
Note 2: I have also traced it through to the internals of Meteor.apply , where it is in fact sending the paramsObject over DDP, in debug session:
// Sends the DDP stringification of the given message object
_send(obj) {
this._stream.send(DDPCommon.stringifyDDP(obj));
}
Many thanks for any insight.
I have created an HTTP POST endpoint for my Meteor server using Iron Router. I would like to send a response back to the requestor with a JSON of the status and some other metadata.
Here is the code for the endpoint:
Router.route('/new_video', {where: 'server'})
.post(function(){
var body = this.request.body;
this.response.setHeader('Content-Type', 'application/json');
var filename = body.filename;
console.log('New video uploaded for: ' + filename);
Meteor.call('newUpload', filename, function(error, results){
if (error){
throw new Meteor.Error("new-video-upload-failed", "New video could not be uploaded.");
var message = {
url: '/new_video',
status: 'success'
};
}
else{
var videoId = results;
console.log('Returned video id: ' + videoId);
var message = {
url: '/new_video',
status: 'failure'
};
}
this.response.end(JSON.stringify(message));
});
});
The Meteor console is printing:
=> Meteor server restarted
I20151002-15:51:26.311(-4)? New recording for: 1422776235,43.46756387,-80.54130886.mp4
I20151002-15:51:26.515(-4)? Returned video id: QiHXxZSb2sn9aNRPs
I20151002-15:51:26.569(-4)? Exception in delivering result of invoking 'newRecording': TypeError: Cannot call method 'end' of undefined
I20151002-15:51:26.569(-4)? at shared/routes.js:79:17
It's a common pitfall of JS where the value of this is modified due to the introduction of another function callback in the Meteor.call.
If you're using Meteor 1.2 which comes with ES2015 arrow functions you can solve the issue using this function declaration syntax instead :
Meteor.call('newUpload', filename, (error, results) => {
// here 'this' will keep referencing the POST route context
// so you can safely use this.response
});
If you're not using Meteor 1.2, use this syntax instead :
Meteor.call('newUpload', filename, function(error, results) {
// inner function is bound to parent function 'this'
}.bind(this));
It already took me several hours implementing cookie on url MeteorJS. What I need to do is that, pass a cookie data n url like 'CURLOPT_COOKIE' PHP. I cant find any example code on their docs and even to forums. For now I have these functions:
/* HTTP REQUEST */
Meteor.methods({
httpRequest: function(type, uri, params){
this.unblock();
check(type, String);
check(uri, String);
check(params, Object);
try {
var result = HTTP.call(type, uri, {params: params});
return result;
} catch (e) {
// Got a network error, time-out or HTTP error in the 400 or 500 range.
return e;
}
}
});
// HTTP request with cooki
getUserDetails: function(session_id, uid){
var params = {
headers: {
Cookie: {
sessid: session_i
}
},
uid: uid
};
var response = Meteor.call('httpRequest', "POST", "http://example.com/rest/wp /alt_wp_resources/loaduser.json", params);
//res = JSON.parse(response.content);
return response;
}
// call here
Meteor.startup(function () {
// delay for 5 sec
Meteor.setTimeout(function (){
Meteor.call('getUserCredentials', 'api12345', '123qweasd', function (error, result) {
// check user authentication
var success = result.success;
console.log(result);
// user has account from lpgp site, let's save him to meteor.
if (success){
console.log('success');
var session_id = result.session_id;
//console.log(_session_id);
Meteor.call('getUserDetails', 'SESSba071091c09f79fefd66e4884dcdde50', 68558, function (error, result) {
if (!error)
console.log(result);
else
console.log(error);
});
}else
// app can't find user account from lpgp site.
console.log(error);
});
}, 5000);
});
The call is successful but, just returned a success: false.
Response:
Object {statusCode: 200, content: "{"success":false}", headers: Object, data: Object}
Meteor's HTTP module on the server side is merely a wrapper for the npm module named request. The request npm module includes support for specifying your own cookies as well as saving them into a cookie jar (just follow the link and search for 'cookie'). The default cookie jar is tough-cookie and interestingly, Meteor includes it even though I don't see any way to use it from Meteor.HTTP.
The upshot of these implementation details is that you can use request directly. I took a similar approach to wrapping request as Meteor's HTTP module but instead of the restricted sub-set of options that HTTP provides, my wrapper allows full access to all the capability of request and tough-cookie. The cool part is that you don't even need to directly add request as a dependency on your own since it's already a dependency of Meteor. The risk, of course, is that a later version of Meteor could use something besides request and your code would break.
Anyway, here is my own wrapper for request. It includes an example of JSessionID cookie support for making Jenkins API calls. Just put this into a file syncRequest.coffee under the \server folder and make sure you have added the coffeescript package (Meteor add coffeescript)... or compile my code and save it to a .js file in the \server folder.
request = Npm.require('request')
populateData = (response) ->
contentType = (response.headers["content-type"] or ";").split(";")[0]
if _.include([ "application/json", "text/javascript" ], contentType)
try
response.data = JSON.parse(response.content)
catch err
response.data = null
else
response.data = null
normalizeOptions = (uri, options, callback) ->
unless uri?
throw new Error("undefined is not a valid uri or options object.")
if (typeof options is "function") and not callback
callback = options
if options and typeof options is "object"
options.uri = uri
else if typeof uri is "string"
options = uri: uri
else
options = uri
return {options, callback}
normalizeResponse = (error, res, body) ->
response = null
unless error
response = {}
response.statusCode = res.statusCode
response.content = body
response.headers = res.headers
populateData(response)
if response.statusCode >= 400
error = makeErrorByStatus(response.statusCode, response.content)
return {error, response}
wrappedRequest = (uri, options, callback) ->
{options, callback} = normalizeOptions(uri, options, callback)
request(options, (error, res, body) ->
{error, response} = normalizeResponse(error, res, body)
callback(error, response)
)
wrappedCall = (method, uri, options, callback) ->
options.method = method
wrappedRequest(uri, options, callback)
wrappedGet = (uri, options, callback) -> wrappedCall("GET", uri, options, callback)
wrappedPost = (uri, options, callback) -> wrappedCall("POST", uri, options, callback)
wrappedPut = (uri, options, callback) -> wrappedCall("PUT", uri, options, callback)
wrappedDelete = (uri, options, callback) -> wrappedCall("DELETE", uri, options, callback)
getWithJSession = (j_username, j_password, securityCheckUri, uri, callback) ->
request = request.defaults({jar: true})
form = {j_username, j_password}
request.post({uri: securityCheckUri, form: form}, (error, response, body) ->
if error?
throw new Error(error)
else if response.statusCode isnt 302
throw new Error("Expected response code 302 (forward). Got #{response.statusCode}")
else
request.get(uri, (error, res, body) ->
{error, response} = normalizeResponse(error, res, body)
callback(error, response)
)
)
syncRequest = Meteor.wrapAsync(wrappedRequest)
syncRequest.call = Meteor.wrapAsync(wrappedCall)
syncRequest.get = Meteor.wrapAsync(wrappedGet)
syncRequest.post = Meteor.wrapAsync(wrappedPost)
syncRequest.put = Meteor.wrapAsync(wrappedPut)
syncRequest.delete = Meteor.wrapAsync(wrappedDelete)
syncRequest.del = syncRequest.delete
syncRequest.getWithJSession = Meteor.wrapAsync(getWithJSession)
syncRequest.getWithJsession = syncRequest.getWithJSession
(exports ? this).syncRequest = syncRequest
Server side:
Meteor.methods({
getFromInstagram: function(){
console.log("called!");
Future = Npm.require('fibers/future');
var myFuture = new Future();
var url = "https://api.instagram.com/v1/media/popular?access_token=[ACCESSTOKEN]";
Meteor.http.get(url, function(error, results){
if(error){
myFuture.throw(error);
} else {
myFuture.return(results);
}
});
console.log( myFuture.wait() );
return myFuture.wait();
}
});
Client side:
instagramContent = Meteor.call("getFromInstagram");
console.log(instagramContent);
The server side console log works and returns an object.
The client side console log in Chrome console returns undefined. What am I overlooking?
Client-side will always be async.
Try something like:
instagramContent = Meteor.call("getFromInstagram", function (error, result) {
console.log(result);
});
From the documentation (http://docs.meteor.com/#meteor_call):
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
I have problem with trying to turn asynchronous function into synchronous.
here is a method from class:
doPost: function(call, data) {
var uri = 'http://localhost/api/'+call;
var api = http.createClient(80, 'localhost');
var domain = 'localhost';
var request = api.request("POST", uri,
{'host' : domain,
'Content-Type' : 'application/x-www-form-urlencoded',
"User-Agent": this.userAgent,
'Content-Length' : data.length
});
request.write(data);
request.end();
request.on('response', function (response) {
response.on ('data', function (chunk) {
sys.puts(chunk);
try {
var result = JSON.parse(chunk);
//------------ the problem
return HOW_TO_RETURN_RESULT;
//------------ /the problem
}catch (err) {
return {'ok': 0, 'err': err}
}
});
});
},
Want to use this function in this way:
result = obj.doPost('getSomeData.php', '&data1=foo&data2=bar');
Reagards
Tom
Simply use callback.
obj.doPost('getSomeData.php', '&data1=foo&data2=bar', function(data) {
result = data;
});
It is impossible to turn an asynchronous function into a synchronous one.
It simply cannot be done.
Instead, you must pass a callback to your function and receive the "return value" in async fashion.
In theory though, you could write some code to block your function from returning until some condition is met (ie, until the async operation is complete), but that would also require the program to be able do other things on another thread while the block is executing, which is probably not possible in node. And even if it were, it would be a world class antipattern and crime against node.js and all things evented and probably summon a velociraptor.
Conclusion: Learn how to work with asynchronous code. Also, you might be interested in reading this question/answer from yesterday (or at least the answer; the question is not very well phrased).