I'm making a simple form in Node.js. Everything else seems to be working correctly, but the function that is supposed to receive post request data is never getting called. Here's the relevant code snippet:
if (request.method == 'POST') {
var body = '';
console.log(request.body);
request.on('data', function (chunk) {
console.log("got the post request data"); //nothing logged to console
body += chunk;
});
request.on('end', onRequestEnd(body, response));
}
The function onRequestEnd does get called, but later my code breaks when there's nothing but an empty string in the parameter body. Is the keyword 'data' correct?
The code was modified from an answer here: How do you extract POST data in Node.js?. I'll post more if needed.
After lots of frustration I solved the problem myself!
I changed the line:
request.on('end', onRequestEnd(body, response));
to:
request.on('end', function() {
onRequestEnd(body, response);
});
It had something to do with callbacks. I'm not exactly sure why this works and the other one doesn't though. This is how I feel: http://www.masti-xpress.com/images/Story-of-Every-Programmer.jpg
I'll share how I solved the problem with this.
I had another view of it however and I'll share that as well.
What I wanted was to have something like this in my "view".
app('/urlToView', function(req, response){
request.on('end', function() {
var post = **request.data;** //sanitize data
resolver.renderTemplateOr404('start.html', post, request, response);
});
}
The request.data is the important thing to notice here.
However I haven't really solved how to "not" have the request.on('end'...) in my view yet.
A reason as to why the console.log() would be how you handle the callback from the function that you do all this work in.
I hijack the request before it lands in my view when I start the server
self.preProcess(self, request, response);
and
preProcess: function onRequest(app, request, response){
processor.preRequest(request);
}
and lastly int the preRequest() function I do
if (request.method === 'POST') {
var postdata = "";
request.on('data', function(postdataChunk){
postdata += postdataChunk;
});
request.on('end', function(){
_transformRequest(request, _transformPostdata(postdata)); //this is to set the data on the request
});
}
and adding a console.log(postdataChunk); here isn't a problem since all of the callbacks are properly handled.
Also, this might be very stupid of me to ask but are you aware of that console.log(); doesnt output to browser but to the terminal window?
This might not be an exact answer for you but I hope this helps a bit.
Related
I am POSTing a json file to a node.js listener, and I must not fully understand how POSTs are properly constructed, because in the code below the on('data') callback is never invoked. I can see the actual json string in the body, so I can work around the lack of a callback, but it seems like I'm doing something wrong with how I generate my POST request. [Postman details further below]
// Server initialization
var server = restify.createServer();
server.use(restify.queryParser());
server.use(CookieParser.parse);
server.use(restify.bodyParser());
// Later, at the point where I register callbacks.
this.server.post('receive', function (request, respond) {
console.log('Received POST');
console.log("Headers: %s", JSON.stringify(request.headers));
console.log("Body: %s", JSON.stringify(request.body));
var body = '' ;
var filePath = './data/notes.json';
// this event is never triggered.
request.on('data', function (data) {
console.log('Data received.');
body += data;
});
request.on('end', function () {
console.log('End of POST');
fs.writeFile(filePath, body, function () {
respond.end();
});
});
});
POST details:
I'm using Postman to create a POST request, Content-Type: application/json and the putting the json string in the raw body. What will normally trigger the 'data' event in a POST request? If I ignore data events and just read from the request body, will I run into issues?
Since you're using restify.bodyParser, that middleware would have already read the request body so there isn't any more for your handler to read (hence no data events). A stream, like request, can only be read once, until it's exhausted. You can't "re-read" it.
Which also means that you can just use request.body, which should be the (parsed) result of the JSON that you're posting.
As an aside: I don't know Postman very well, but it looks like you're sending a JSON-encoded string to the server, as opposed to a JSON-encoded object.
To send the latter, I would expect that this should be the raw body data:
{"body":"this is a test"}
We're building a node.js application using Express, but are separating our layers. So the browser is running pure jQuery and javascript, the web server and application server are Node.js and Express. We're using REST APIs between them all.
We're using jQuery 1.10.2
Since the application server cannot be open to the public, the browser must make API calls to the web server, which manages making the call to the application server and returning the results. Here is what that looks like...
// Proxy all other API calls to the backend server
var request = require('request');
app.all('/api/*', function (req, res) {
var targeturl = apihost+req.originalUrl;
console.log("Proxy: "+targeturl);
request({
url: targeturl,
method: req.method,
json: req.body
}, function (error, response, data) {
if(error) {
res.send(error);
res.end();
} else {
console.log("SUCCESS...");
console.log(data);
res.send(data);
res.end();
}
});
});
Our data is a two-level hierarchy of Flows and one or more child Milestones on each Flow. When someone clicks the Save link on our page to save a Flow and its Milestones, the Javascript pulls the Flow data from the page, then makes an AJAX call to save the Flow information first, then waits for the "done()" handler before pulling the Milestone information from the page and making a second AJAX call to save that data. Here is the saveFlow() function executed in the browser...
function saveFlow(organization_id, user_id, flow_id) {
var name = $('#Name').val();
var purpose = $('#Purpose').val();
console.log("Trying to save flow: " + flow_id);
if (name && name.length > 0) {
$.ajax({
url: "/api/flow",
type: 'POST',
datatype: 'json',
data: {
flow_id: flow_id,
name: name,
purpose: purpose,
organization_id: organization_id,
user_id: user_id,
timestamp: (new Date())
}
})
.fail(function (error) {
console.log("Could not save the flow: " + error.message);
})
.done(function (flow) {
console.log("Saving milestones for flow "+flow.id+" ["+flow_id+"]");
saveMilestones(flow.id);
});
} else {
console.log("Refusing to save a flow that has no name");
}
}
The proxy code shows me that this call succeeded...
SUCCESS...
{ success: true,
message: 'Flow Updated!',
id: '56de8e346d229b492a0954f9' }
But the proxy code also demonstrates that the next API call never takes place. Moreover, the log statement in the Done block is never executed, nor are any log statements placed inside the saveMilestones() function.
Clearly, the .done() handler is never being called in this case. I use the done() handler in other parts of my code and it works. I've compared the code but don't see any differences. The proxy code we're using is driving every other API call made from our pages successfully, so I don't think that it's failing to return the success status.
I've done a lot of searching on this site and tried every suggestion I could find - adding a explicit "json" datatype parameter, switching from ":success" to ".done()" - but nothing has worked yet. I even tried switching from done() to always() - a bad idea but I wanted to see if it would get called...and it wasn't called.
It's also worth noting that the .fail() handler is also not being executed.
Is it possible that the JSON block we're sending back to indicate success is somehow failing to make jQuery realize that we were successful? Or is it possible that the AJAX call is crashing when it tries to process the successful return? If so, how do I catch that to prove it and fix it?
Hope someone can help.
I'm trying to get info from my database via an AJAX request, but the success event isn't triggered.
The AJAX request is however received by the server, as it trigger the correct console log in the terminal.
I'm building it in Node, using the Express framework.
Here is the code for triggering the AJAX call. The 'componentDidMount triggered' is logged to the console. But then it stops. Neither the console.log in 'success' or 'error' is triggered, even though I know that the server has receenter code hereived an AJAX request.
app.get('/api/:name', function (req,res){
name = req.params.name;
Member.findOne({username: name}, function(err,member){
res.setHeader('Content-Type', 'application/json');
res.write(member.toString());
console.log('member fetched from DB: ', member);
res.end();
});
});
I'm not getting any other errors either, so I have no idea why this isn't working.
Can anybody please help?
UPDATE: Thanks for the help. I'm one step closer now. I did not realise I needed 'res.end' when serving the AJAX request. When I changed this, I've managed to get a response, though it's an error.
Her is the server side code:
app.get('/api/:name', function (req,res){
name = req.params.name;
Member.findOne({username: name}, function(err,member){
res.setHeader('Content-Type', 'application/json');
res.write(member.toString());
console.log('member fetched from DB: ', member);
res.end();
});
});
Tangential to your question, but equally important as figuring out why .success isn't being called: your code is not calling Main's setState in the success handler, because the success function isn't called "in your component" but "by jQuery, somewhere".
If you want to call setState on your component as part of the success handling, either cache the component and then call component functions on that:
var self = this;
$.ajax({
...
success: function() {
self.setState({ ... });
}
...
or use the JavaScript bind function (unrelated to jQuery.bind) to explicitly set the execution context for the success function to be your component:
...
success: function(data) {
console.log('success')
this.setState({data: data});
}.bind(this),
...
My mistake was in the res.write(member.toString()). I had to change it to res.write(JSON.stringify(member)), as the client was asking for a JSON file.
In my node.js webpage I'm making a page preview similar to the Facebook link preview. I'm making a call to get the html of the page, and use it to create the preview.
$.ajax({
type: 'GET',
data: { "html": url },
url: "/htmlTest",
success: function (data) {
imgArray = [];
$('img', data).each(function () {
imgArray.push(this.src);
});
...
This is the server-side code that handles the request.
app.get('/htmlTest', function (req, res) {
res.writeHead(200, { 'content-type': 'text/html' });
request(req.query.html, function (error, response, body) {
if (error) {
res.write(error.toString());
res.end('\n');
}
else if (response.statusCode == 200) {
res.write(body);
res.end('\n');
}
})
});
Now what I've been noticing, is that it will just insert any css the other page uses into my page, which can really screw everything up. Why is this happening?
Also, while I'm at it, does anyone have any better ideas for a facebook-style page preview?
No. writeHead writes HTTP headers to the underlying TCP stream. It has absolutely nothing to do with HTML.
You're running into an issue because your server returns the wholesale HTML content of the requested URL. You then pass this string into jQuery, which is apparently adding contained CSS styles into your document.
Generally, it is a terrible idea to take random code from a user-supplied URL and run in the context of your page. It opens you to gaping security holes – the CSS artifacts you're seeing are one example.
To be blunt, your code has numerous problems, so bear with me as I point out some issues.
app.get('/htmlTest', function (req, res) {
res.writeHead(200, { 'content-type': 'text/html' });
Here, you respond to the browser with a success status (200) beore your server actually does anything. This is incorrect: you should only respond with either a success or error code after you know if the request succeeded or failed.
request(req.query.html, function (error, response, body) {
if (error) {
res.write(error.toString());
res.end('\n');
}
Here would be a good place to respond with an error code, since we know that the request did actually fail. res.send(500, error) would do the trick.
else if (response.statusCode == 200) {
res.write(body);
res.end('\n');
}
And here's where we could respond with a success code. Rather than use writeHead, use Express's set and send methods – things like Content-Length will be correctly set:
res.set('Content-Type', 'text/html');
res.send(body);
Now what happens if response.statusCode != 200? You don't handle that case. error is only set in the case of network errors (such as inability to connect to the target server). The target server can still respond with a non-200 status, and your node server would never respond to the browser. In fact, the connection would hang open until the user kills it. This could be fixed with a simple else res.end().
Even with these issues resolved, we still haven't addressed the fact that it's not a good idea to try to parse arbitrary HTML in the browser.
If I were you, I'd use something that parses HTML into a DOM on the server, and then I'd return only the necessary information back to the browser as JSON. cheerio is the module you probably want to use – it looks just like jQuery, only it runs on the server.
I'd do this:
var cheerio = require('cheerio'), url = require('url'), request = require('request');
app.get('/htmlTest', function(req, res) {
request(req.query.url, function(err, response, body) {
if (err) res.send(500, err); // network error, send a 500
else if (response.status != 200) res.send(500, { httpStatus: response.status }); // server returned a non-200, send a 500
else {
// WARNING! We should probably check that the response content-type is html
var $ = cheerio.load(body); // load the returned HTML into cheerio
var images = [];
$('img').each(function() {
// Image srcs can be relative.
// You probably need the absolute URL of the image, so we should resolve the src.
images.push(url.resolve(req.query.url, this.src));
});
res.send({ title: $('title').text(), images: images }); // send back JSON with the image URLs
}
});
});
Then from the browser:
$.ajax({
url: '/htmlTest',
data: { url: url },
dataType: 'json',
success: function(data) {
// data.images has your image URLs
},
error: function() {
// something went wrong
}
});
For some reason this.collection.create() is returning an error, but interestingly, the error message seems like the model I just added to my collection.
this.collection.create({
name: $('#big-input').val(),
firstRemark: $('#small-input').val(),
postedBy: window.user.displayName,
twitterHandle: window.user.twittername,
pictureUrl: window.user.profilePic
},{wait: true,
success: function(response){
console.log("success");
console.log(response)
},
error:function(err){
console.log(err)
}
});
this is what I get after console.log(err):
exports.newPost = function(req, res){
console.log("GOT NEW TWEET REQUEST");
var newPost = new Post(req.body)
newPost.dateCreated = new Date();
newPost.save();
res.send(200);
};
Thanks to answers below I was able to print my 'real' error. As seen below xhr.responseText is 'OK' and 'status' is 200. Why is this response triggering a success but an error?
I also have a parse method in my collection
parse: function(response){
this.page = response.page;
this.perPage = response.perPage;
this.total = response.total;
this.noofpages =response.noofpages;
return response.posts;
},
This is expected. Have a look at the Model.Save documentation, which says the error callback will be called with (model, xhr, options) as it's parameters.
If you want the actual contents of the response you can get it from the second parameters responseText property: xhr.responseText. There's more details on jqXHR elements in the jquery documentation: http://api.jquery.com/jQuery.ajax/#jqXHR
The parameters of your success callback are also not quite right - it takes (model, response, options)
EDIT:
Not entirely sure if it's the cause of your problems, but your server should be returning a 200 status code and the models JSON on success. Have a look at the Backbone.Sync documentation.
From looking at the code this does look important when passing wait: true as an option, as the attributes set are extended with the returned attributes from the server. Have a look at the options.success function used by backbone here to see what I mean. It certainly looks like something might go wrong if you return "OK" from the server, though I'm not sure if it'd be exactly the problem you're experiencing.
EDIT2: Slight correction to what I wrote above: The return value gets passed through Model.parse. Since you've defined a custom parse function, the server should return something that is going to work with it, rather than just plain JSON for the model.
The error function will receive 3 arguments as defined in the wrapError function in backbone.js
model.trigger('error', model, resp, options);
Therefore what your output is, is correct. You will want to add a variable to your error function to capture the response (2nd argument) and log that instead to help you debug.