How finish res without change page? - node.js

With res.end() or res.send() the result is a blank page, so how to finish without changing the page? My code is the following:
router.post('/subirArchivo/:idProducto', function (req, res){
var idProducto = req.params.idProducto;
var form = new formidable.IncomingForm();
var dir = '../../../../uploads/'+idProducto+'/';
form.parse(req);
form.on('fileBegin', function (name, file){
checkDirectorySync(path.join(__dirname, dir));
file.path = path.join(__dirname,dir, file.name);
});
form.on('file', function (name, file){
console.log('Uploaded ' + file.name);
});
res.end;});

This is a client-side issue, not a server issue because it is the client that determines how this works.
If you let the browser submit a form on its own (normal form post submission), then it will be expecting a response back from the POST that it will show in the browser. You cannot change that if it is an automatic form submission.
If you use an Ajax call to post the data to the server and you prevent the default form post, then the response comes back to your Javascript and you can do anything you want with the returned response (including nothing). The page contents will not change on their own.

Related

Error: Can't set headers after they are sent - Ajax, Node, Express

I'm trying to using Airtable, node.js, express.js and jquery to create a simple user authentication functionality but I'm fairly new at this and I'm running into a problem I can't seem to fix and the articles I've read I can't seem to grasp or adapt to my particular situation.
I have this Ajax call in my html doc:
$("#checkUser").submit(function(e) {
var studentID = $('input[name="student"]').val()
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(), // serializes the form's elements.
success: function(data) {
$(window).attr("location", window.location.href + 'Dashboard?student=' + studentID);
},
error: function(data){
console.log("User not found. Try again");
}
});
});
This call sends the inputted username and data to the server which then processes it in the following way:
app.post('/checkUser', urlencodedParser, function(request,response){
var user = JSON.stringify(request.body);
user = JSON.parse(user);
base('RegisteredStudents').select({
filterByFormula: '{UserID} = ' + user.student,
view: "Grid view"
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
response.sendStatus(200);
});
fetchNextPage();
}, function done(error) {
response.sendStatus(404);
});
});
If the user exists in the database of Airtable, it should send '200' which the Ajax then reacts by redirecting accordingly to the user's profile. Otherwise, if the user does not exist, the server should respond with '404', which the Ajax call should react to by printing a statement in the console. While it does do these two things well, the server breaks down when, after a student puts in the wrong user ID and the Ajax prints the statement, the student tries to put once more a userID. I get the " Can't set headers after they are sent. " message. Please, how can I solve this?
Thank you!
You have two response.send..., you can only send data once. Either make sure only one runs with some conditional or add return before all response.send... so if any of them runs, the program will return and the other response.send.. will not run.

how to pass data and redirect in express

My code is
main.post('/userlist', function(req, res, next) {
// where did you get this?
// var db = req.db;
var query = connection.query("SELECT name,zaman,giriscikis FROM giriscikis where date_format(zaman,'%Y-%m-%d') between ? and ?", [req.body.bas, req.body.bitis], function(err, result) {
if (err) throw err;
console.log(result);
res.send(httpResponse(result));
return res.redirect('/zamansorgu');
//console.log(req.body.bas)
//console.log(req.body.bitis)
});
});
I want to fecth data from database and redirect to same page in the code(zamansorgu.html)
But I get an error
Cannot set headers after they are sent to the client
How can I solve this problem
thank you for your helps
You are attempting to send back JSON data and redirect to a different page. That's not possible. Each endpoint request can have one response, not more. You can either send back the data, or redirect. That's because redirecting really does send back data too (the html of the new target page).
Think about it from the caller's point of view. If it did allow this how would it work? If someone uses this link from a browser should the browser show the JSON data you returned, or should it take the user to the new page?
The error is saying "hey, I already sent back data. I can't redirect now because we are already down the path of returning some JSON".
If you want to use the data to format the output that can be done, or if you want to redirect to a new location and pass the data in the url, that's also possible. Consider code like this:
main.post('/userlist', function(req, res, next) {
// var db = req.db;
var query = connection.query("SELECT name,zaman,giriscikis FROM giriscikis where date_format(zaman,'%Y-%m-%d') between ? and ?", [req.body.bas, req.body.bitis], function(err, result) {
if (err) return next(err);
if (result.urlparam) {
// this builds a new url using the query value
const nextUrl = `/zamansorgu?param=${result.urlparam}`;
return res.redirect(nextUrl);
}
else {
// this builds html here
const html = `<html><body><h1>${result.title}</h1></body></html>`;
return res.send(html);
}
});
});
I also ran into this, in my case it was quite a deceptive little bug. A node-inspector session helped me pinpoint the problem quickly however. The problem in my case was pretty bone-headed, the res.end call in the sample below is the offending line.
res.writeHead(200, {"Content-Type": "application/json"});
res.end(JSON.stringify(someObject));
someObject did not exist after a refactor and that was causing a ReferenceError to get thrown. There is a try-catch in Router.prototype._dispatch that is catching the ReferenceError and passing it into next
res.status(301).redirect(`/zamansorgu?name=${"John"}&email=${"john#email.com"}`)
So, this is something I explored but it will be dependent on the structure of your application. You could always pull the data out using query params and hydrate your application.

Node.js - Stream Binary Data Straight from Request to Remote server

I've been trying to stream binary data (PDF, images, other resources) directly from a request to a remote server but have had no luck so far. To be clear, I don't want to write the document to any filesystem. The client (browser) will make a request to my node process which will subsequently make a GET request to a remote server and directly stream that data back to the client.
var request = require('request');
app.get('/message/:id', function(req, res) {
// db call for specific id, etc.
var options = {
url: 'https://example.com/document.pdf',
encoding: null
};
// First try - unsuccessful
request(options).pipe(res);
// Second try - unsuccessful
request(options, function (err, response, body) {
var binaryData = body.toString('binary');
res.header('content-type', 'application/pdf');
res.send(binaryData);
});
});
Putting both data and binaryData in a console.log show that the proper data is there but the subsequent PDF that is downloaded is corrupt. I can't figure out why.
Wow, never mind. Found out Postman (Chrome App) was hijacking the request and response somehow. The // First Try example in my code excerpt works properly in browser.

how to publish a page using node.js

I have just begun to learn node.js. Over the last two days, I've been working on a project that accepts userinput and publishes a ICS file. I have all of that working. Now consider when I have to show this data. I get a router.get to see if I am at the /cal page and..
router.get('/cal', function(req, res, next)
{
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
//console.log(ical.toString());
});
});
res.send(ical.toString());
// res.render('index', {
// title: 'Cal View'
// })
})
So when /cal is requested, it loops through my db and creates an ICS calendar ical. If I do console.log(ical.toString) within the loop, it gives me a properly formatted calendar following the protocol.
However, I'd like to END the response with this. At the end I do a res.send just to see what gets published on the page. This is what gets published
BEGIN:VCALENDAR VERSION:2.0
PRODID:calendar//EN
END:VCALENDAR
Now the reason is pretty obvious. Its the nature of node.js. The response gets sent to the browser before the callback function finishes adding each individual VEVENT to the calendar object.
I have two related questions:
1) Whats the proper way to "wait" till the callback is done.
2) How
do I use res to send out a .ics dynamic link with
ical.toString() as the content. Do I need to create a new view for
this ?
edit: I guess for number 2 I'd have to set the HTTP headers like so
//set correct content-type-header
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
but how do I do this when using views.
Simply send the response, once you got the neccessary data! You are not required to end or send directly in your route but can do it in a nested callback as well:
router.get('/cal', function(req, res, next) {
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
});
res.type('ics');
res.send(ical.toString());
});
});
I also included sending the proper Content-Type by using res.type.
Also: Don't forget to add proper error handling. You can for example use res.sendStatus(500) if an error occured while retrieving the documents.

Sending text to the browser

I have managed to get file uploading work in Node.js with Express, and in the code i'm checking whether it's an image or not that the user is trying to upload.
If the file was successfully uploaded I want to show a message to the user, directly to the HTML page with the uploading form. The same should be if the file the user tried to upload wasn't an image, or something else happened during the upload.
The code below works (res.send...) but it opens up a new page containing only the message.
My question is: How can I change my code so that the message is sent directly to the HTML page instead? If it could be of any use, i'm using Jade.
Thanks in advance!
app.post('/file-upload', function(req, res, next) {
var fileType = req.files.thumbnail.type;
var divided = fileType.split("/");
var theType = divided[0];
if (theType === "image"){
var tmp_path = req.files.thumbnail.path;
var target_path = './public/images/' + req.files.thumbnail.name;
fs.rename(tmp_path, target_path, function(err) {
if (err) throw err;
fs.unlink(tmp_path, function() {
if (err) {
throw err;
res.send('Something happened while trying to upload, try again!');
}
res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
});
});
}
else {
res.send('No image!');
}
});
from what I understand you are trying to send a message to an already open browser window?
a few things you can do,
Ajax it, send the post, and process the return info.
Submit it as you are doing now, but set a flash message (look at http://github.com/visionmedia/express-messages) and either res.render the form page, or res.redirect to the form function
now.js or a similar solution. This would let clientside use serverside functions and serverside code to run clientside functions. So what you would do would be on submit, pass the post values to a serverside function, which will process it and trigger a clientside function (display a message)
For my money option #2 is probably the safest bet, as clients without javascript enabled will be able to use it. As for usability #1 or #3 would give a more streamlined appearance to the end user.
You can use WebSockets. I recommend using Socket.IO, it's very easy to work with. On the client-side you would have an event-handler which would use JavaScript to append the new information to that page.
You could then have the server for example say:
socket.emit('error', "Something happened while trying to upload, try again!");
and the client would use:
socket.on('error', function(data){
//alert?
alert(data);
});
http://socket.io/#how-to-use

Resources