Iron Router Server Side Routing callback doesn't work - meteor

I am newer for IronRouter, why readFile callback executed the response are send to client.
Router.map(
()->
this.route 'readFile',
path: '/readFile'
where: 'server'
method: 'GET'
action: ()->
self = this
fs.readFile '/tmp/a.txt', (err, data)->
if err
throw err
console.log(data.toString())
self.response.writeHead(200, {'Content-Type': 'text/plain'})
self.response.end(data)
console.log('response ...')
)
http.js:733
W2049-12:04:26.781(8)? (STDERR) throw new Error('Can\'t render headers after they are sent to the client.'
W2049-12:04:26.781(8)? (STDERR) ^
W2049-12:04:26.782(8)? (STDERR) Error: Can't render headers after they are sent to the client.
But, I use express , like this is work well.
exports.index = function(req, res){
fs.readFile('/tmp/a.txt', function (err, data) {
if (err) throw err;
console.log(data.toString());
res.send(200, data.toString());
});
console.log('response ...');
};
thanks #ChristianF #Tarang Use Meteor._wrapAsync or Future all work well . when I use self define function replace fs.readFile. It take throw ex . I Doubt My defined function has error. As follows:
#getAccounts = (callback) ->
query = "SELECT Id, Name, AccountNumber FROM Account"
queryBySoql(query, (err, result)->
if err
return console.error(err)
return callback(result)
)
I invoked link this:
# using Meteor
#data = Meteor._wrapAsync(getAccounts)()
#using Future
waiter = Future.wrap(getAccounts)()
data = waiter.wait()
this.response.writeHead 200, {'Content-Type': 'text/plain'}
this.response.end JSON.stringify(data)
thanks all.

Just today I struggled with this very issue. The answer, it seems to me, is that meteor (or at least the iron router) doesn't handle async calls the way you'd expect. The solution is to wrap the async call into a fiber, which is the mechanism meteor uses to keep the programming synchronous.
In your case try this (sorry, I don't know coffeescript very well):
var Future = Npm.require('fibers/future');
...
var waiter = Future.wrap(fs.readFile);
var data = waiter('/tmp/a.txt').wait();
response.writeHead(200, {'Content-Type': 'text/plain'})
response.end(data)
EDIT Addressing the addition to the question.
Functions wrapped in a future need to have a callback as their last argument that has the (err, result) signature. Try this:
#getAccounts = (callback) ->
query = "SELECT Id, Name, AccountNumber FROM Account"
queryBySoql(query, (err, result)->
if err
return console.error(err)
return callback(err, result)
)

You could wrap up your read file request's callback into the same fiber. It will not block other requests & comes out quite clean.
readFile = Meteor_wrapAsync(fs.readFile.bind(fs))
data = readFile("/tmp/a.txt")
console.log data
#response.writeHead(200, {'Content-Type': 'text/plain'})
#response.end data
return

Related

Meteor Async ValidatedMethod gets called with function parameters undefined

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.

'end' undefined for this.response in Iron Router for Meteor

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));

passing cookie data on MeteorJS HTTP request

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

Meteor async call to Instagram API not returning on client side. Returns on server side

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.

NodeJS: problem with request asynchronous, synchronous, api

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).

Resources