-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-json response to URLs incorrectly handled #26
Comments
This is most likely caused by danwrong/restler#110 |
Hello, I am a node.js & express.js beginner Please tell me the solution. Is it cause from graph API and JSON data? Thank you for your thoughts ! undefined:1 SyntaxError: Unexpected token S |
@otogen: Did you read the comment I made 3 days ago? This is due to an unfixed bug in restler. |
Totally yes |
I "was" experiencing this issue with Heroku and facebook. Here is a sample of the change I made: (// <<<<<)
|
And with these 2 changes, login seems to work also:
It seems to me that the parse_signed_request function is not in synch (API-wise) with FaceplateSession "constructor" and the chain of callbacks from middleware. Maybe? |
Thank you Henddher San ! I will check it soon and report you here soon ! |
Henddher San, I have just check the program and it works ! So great, thank you so much Henddher San !!!
|
Machunter (in another post danwrong/restler#114) confirmed the issue we are seeing: JSON.parse failing to parse the response. Thus, JSON.stringify followed by JSON.parse "solves" the problem. Another strange thing is that adding JSON.stringify to the parse_signed_request function does NOT work. JSON.stringify seems to be "needed" for function get and fql only. Can anyone knowledgable with Restler and Faceplate share some thoughts about the "solution" we are discussing? I am no expert of neither Restler, Faceplate, nor Node as a matter of fact. The most strange thing, IMO, it's the fact that the issue started happening without changes to the code of the apps. So, either Facebook responses changed or Node & NPM versions used on Heroku changed (to a different version which exposes faceplate and restler issues? - Node 0.10?). In my package.json, I had declared engines as follows: "engines": { That declaration apparently allows newer versions of Node and NPM to be used if available. Now that I know which exact versions are being used, I modified my package.json as follows: "engines": { However, I don't know if Heroku infrastructure honors specific versions either. Can anyone share some light about JSON.parse? I still think there's an issue with faceplate MASTER/HEAD and how "Errors" are created and passed onto FaceplateSession constructor when these errors arise during Restler and/or JSON.parse operations. |
I don't know which version of node made it actually break, but danwrong/restler#110 has been wrong forever - Every instance of a restler handler you create shares the same EventEmitter. Why I got my error:
I'm not sure what you're seeing here, but this bug is fixed for me after applying danwrong/restler#113 and using faceplate/master. |
Nope. All you're doing there is reverting the changes made to faceplate/0.5.0. These changes are sound - the problem is the other code, which I fixed in #22, #23, and #25. You'll need to update your API calls - 0.5.0 is an API-incompatible update. |
Thank you Eric. How can I use Faceplate master and danwrong/restler#113 I am using Heroku but I do NOT know if Heroku honors node module versions specified in package.json Anyone knows? How could I specify these 2 different versions of Faceplate and Restler? "dependencies": { "engines": { |
@Henddher: I can't comment - I haven't been testing with node 0.8.21. Most likely, your problem is that 0.5.0 is a non-working release of faceplate, and the commits I made on top haven't been put into a 0.5.1 on npm. Until then, you could revert to 0.4. |
Thanks Eric. My package.json had 0.8.x and 1.1.x initially. I even tried node 0.8.21 and 0.8.19 and the app still fails with the same JSON.parse issue. Either Heroku does not honor package.json versions of dependencies and/or engines or Facebook JSON responses changed somehow. I can't think of any other possible explanation for the sudden breakage. Only calling JSON.stringify before JSON.parse seems to successfully solve the issue. Keep in mind that I tested creating a brand new Heroku app from Facebook developer site and this sample app ALSO fails exactly the same way as the others. Are there any combination of versions (node, faceplate and restler) in which the issue does not happen? |
I've been having the problem since last week and yet I don't have any calls to JSON.parse() in my code. I'm really no expert with Heroku or Node.js but I tried looking for code similar to the excerpt given in the solution posted, and I could find references to get and fql; I changed my code like so:
This was the only code I could find with get and fql calls, apart from the below, which I haven't changed:
The changes I did make haven't worked, but there were no calls to JSON.parse() in my code before, so I wasn't sure what to do; I do have calls to JSON.stringify() for working with my Postgres database, but I've never had a problem with those before... (but could they have caused a problem?) |
Actually, the changes I produced to bypass the problem are not done to the "example heroku app" but to Faceplate itself. IOW, you need to copy faceplate module to your "example app" and add the JSON.stringify to the JSON.parse calls - not all of them but only the ones I cited. To use my own version of faceplate, I simply copied faceplate.js (from faceplate node module) onto my app source tree and updated the "require('faceplate')" statement in the example app to point to my own copy of faceplate. I am varely familiar with Node.js myself so I don't know if there are better ways to do this. Keep in mind that this is NOT the real solution. According to Eric Wieser, the real issue lies in Restler. Why this issue happens? I truly don't know either. I speculate this started happening because something changed in Facebook side and its graph responses are different causing the Restler issue to appear. |
Ok, so here is what I've done:
and |
This is the faceplate.js I am using. I placed it in the same dir where web.js is at. var b64url = require('b64url');
var crypto = require('crypto');
var qs = require('querystring');
var restler = require('restler');
var Faceplate = function(options) {
var self = this;
this.options = options || {};
this.app_id = this.options.app_id;
this.secret = this.options.secret;
this.middleware = function() {
return function(req, res, next) {
if (req.body.signed_request) {
self.parse_signed_request(req.body.signed_request, function(decoded_signed_request) {
req.facebook = new FaceplateSession(self, decoded_signed_request);
next();
});
} else if (req.cookies["fbsr_" + self.app_id]) {
self.parse_signed_request(req.cookies["fbsr_" + self.app_id], function(decoded_signed_request) {
req.facebook = new FaceplateSession(self, decoded_signed_request);
next();
});
} else {
req.facebook = new FaceplateSession(self);
next();
}
};
};
this.parse_signed_request = function(signed_request, cb) {
var encoded_data = signed_request.split('.', 2);
var sig = encoded_data[0];
var json = b64url.decode(encoded_data[1]);
var data = JSON.parse(json);
// check algorithm
if (!data.algorithm || (data.algorithm.toUpperCase() != 'HMAC-SHA256')) {
cb(new Error("unknown algorithm. expected HMAC-SHA256"));
return;
}
// check signature
var secret = self.secret;
var expected_sig = crypto.createHmac('sha256', secret).update(encoded_data[1]).digest('base64').replace(/\+/g,'-').replace(/\//g,'_').replace('=','');
if (sig !== expected_sig) {
cb(new Error("bad signature"));
return;
}
// not logged in or not authorized
if (!data.user_id) {
cb(data);
return;
}
if (data.access_token || data.oauth_token) {
cb(data);
return;
}
if (!data.code) {
cb(new Error("no oauth token and no code to get one"));
return;
}
var params = {
client_id: self.app_id,
client_secret: self.secret,
redirect_uri: '',
code: data.code
};
var request = restler.get('https://graph.facebook.com/oauth/access_token',
{ query:params });
request.on('fail', function(data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(result);
});
request.on('success', function(data) {
cb(qs.parse(data));
});
};
};
var FaceplateSession = function(plate, signed_request) {
var self = this;
this.plate = plate;
if (signed_request) {
this.token = signed_request.access_token || signed_request.oauth_token;
this.signed_request = signed_request;
}
this.app = function(cb) {
self.get('/' + self.plate.app_id, function(err, app) {
cb(err,app);
});
};
this.me = function(cb) {
if (self.token) {
self.get('/me', function(err, me) {
cb(err,me);
});
} else {
cb(null,null);
}
};
this.get = function(path, params, cb) {
if (cb === undefined) {
cb = params;
params = {};
}
if (self.token)
params.access_token = self.token;
try {
var request = restler.get('https://graph.facebook.com' + path,
{ query: params });
request.on('fail', function(data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(result);
});
request.on('success', function(data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(null, result);
});
} catch (err) {
cb(err);
}
};
this.fql = function(query, cb) {
var params = { access_token: self.token, format:'json' };
var method;
var onComplete;
if (typeof query == 'string') {
method = 'fql.query';
params.query = query;
onComplete = function(res){
var result = JSON.parse(JSON.stringify(res)); // <<<<
cb(null, result.data ? result.data : result);
};
}
else {
method = 'fql.multiquery';
params.queries = JSON.stringify(query);
onComplete = function(res) {
if (res.error_code)
return cb(res);
var data = {};
res.forEach(function(q) {
data[q.name] = q.fql_result_set;
});
cb(null,data);
};
}
var request = restler.get('https://api.facebook.com/method/'+method,
{ query: params });
request.on('fail', function(data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(result);
});
request.on('success', onComplete);
};
this.post = function (params, cb) {
var request = restler.post(
'https://graph.facebook.com/me/feed',
{query: {access_token: self.token}, data: params}
);
request.on('fail', function(data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(result);
});
request.on('success', function (data) {
var result = JSON.parse(JSON.stringify(data)); // <<<<
cb(null, result.data ? result.data : result);
});
};
};
module.exports.middleware = function(options) {
return new Faceplate(options).middleware();
}; Then, in web.js: var faceplate = require('./faceplate'); This declaration will make the app to use the hacked faceplate.js instead of the real one in node_modules which comes from package.json dependency. IMPORTANT: This is what works for me. I cannot guarantee that it will work for anyone else. |
Thank you! It finally works! :D I used the 0.0.4 version (which is quite different in some parts), so if anyone wants that version with changes made here you go (http://pastebin.com/FfV3eJSx). Also, it should be noted that it works if you make the changes to the Thanks again - this issue has been bugging me for over a week! |
Got this traceback just now:
From this line
For whatever reason, a graph URL is returning a non-JSON response fo a request to
/me
. I'm not sure if this is documented behaviourThe text was updated successfully, but these errors were encountered: