res.sendFile() not sending file (no errors) - node.js

I have a simple login page using passport.js.
$("#loginUser").on("click", function () { //login button handler
var User = {
username: $('#username').val().trim().toLowerCase(),
password: $("#password").val().trim()
};
//$("#password").val("");
$.post("/login", User);
});
The code for the post route for "/login" is below (taken from passport docs):
app.post('/login', function (req, res, next) {
passport.authenticate('local', function (err, user) {
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
req.logIn(user, function (err) {
if (err) { return next(err); }
return res.redirect('/home/' + user.user_id);
});
})(req, res, next);
});
Below is the code for the "home" route:
app.get("/home/:user?", function (req, res) { //home page
console.log(req.params.user)
if (req.user) {
console.log(path.join(__dirname, "../public/home.html"))
res.sendFile(path.join(__dirname, "../public/home.html"), (err) => {
if (err) {
console.log(err);
} else {
console.log('file sent!');
}
});
}
else {
console.log("no user");
res.sendFile(path.join(__dirname, "../public/login.html"));
}
});
The issue I'm having is when the page is supposed to redirect to the home page, it just does nothing. The console.log("file sent") will trigger with no errors, even though nothing happens and the page stays on the login screen. However, if I physically type in localhost://8080/home the page will then load perfectly fine.

It's not a matter of your server not sending the file - it's a matter of your web page isn't redirecting properly.
Ajax calls which you are using with this code:
$.post("/login", User);
do not redirect automatically. All they do is return the 302 response to your Ajax completion handler Javascript (which you don't have any code for). So, your request is delivered to the server. Your server sends a 302 redirect response, that 302 response comes back to your Javascript and gets dropped on the floor by your Javascript.
Only URLs put in the browser bar or URLS clicked on by the user or browser-only form posts (not done from Javascript) will redirect automatically. Ajax calls do not redirect automatically - they just deliver the redirect status and location back to your Javascript where your Javascript has to then process it in order to do anything.
You have a couple of choices to fix:
Switch to a plain browser form post with no Javascript and then the browser itself will see and follow the redirect.
Use a completion handler for your Ajax call, check the status and if it's a 3xx response, then get the Location header and set window.location = xxx to the location in that header to cause the redirect to happen.
I haven't tested this, but here's the general concept for checking the status and manually redirecting based on a 3xx status.
$.post("/login", User).then((data, textStatus, jqXHR ) => {
if (jqXHR.status >= 300 && jqXHR.status <= 399) {
window.location = jqXHR.getResponseHeader("Location");
} else {
console.log("not a redirect status of:", jqXHR.status);
}
});

Related

NodeJS Express Routing - Only first request gets rendered

In the server script I try to deliver different html files. When app.post('/login'...) comes in, res.sendFile() is working and the html gets rendered. On the second call, whenn app.get('/go') comes in, the file gets served, but not displayed. I cannot explain why the second HTML file is not displayed. What am I doing wrong?
the second request comes from a fetch request in a javascript
socket.on('gameStarted', (data) => {
console.log("Game started");
fetch('/go', {method: 'GET'});
})
served but not displayed
app.post('/login', async (req, res, next) => {
var roomNR = req.body.player.gameCode;
var playerName = req.body.player.nickname;
var codeValid = await checkCode(activeRoomsCollection, gameCodes, roomNR);
var playerExists = await playerCollection.findOne({ playerName: playerName })
if (codeValid) {
if ((playerExists === null) || !playerExists) {
playerCollection.insertOne({ room: roomNR, playerName: playerName, state: false });
console.log(`Added player '${playerName}' with roomnumber '${roomNR}'`);
res.sendFile(path.join(__dirname + '/../../public/lobby.html'), function (err) {
if (err) {
console.log(err);
res.status(err.status).end();
}
else {
console.log('Sent Lobby');
}
});
} else {
// updateDomElement(player, elementId, data)
//res.send('Benutzername existiert bereits');
}
} else {
res.send('Code ungültig');
}
});
app.get('/go', (req, res, next ) => {
res.sendFile(path.join(__dirname + '/../../public/raetsel1.html'), function (err) {
if (err) {
console.log(err);
res.status(err.status).end();
}
else {
console.log('Sent Raetsel1');
}
});
});
fetch() never displays anything on its own. It's a way for your Javsascript to issue http requests to remote servers and those servers then return content back to your Javascript. The result from those http requests ONLY goes to your Javascript. Nothing in the view of the page is affected at all by a fetch() call.
If you want the result of a fetch() call to display something in your page, you would need to write Javascript to do that (to insert content into the current page).
If, instead, you just want the browser to go to a new page, then change from this:
fetch('/go', {method: 'GET'});
to this:
window.location = "/go";
This will cause the browser to go to the URL, retrieve the content and display it. This will shut-down the current page and load and display a new page and the URL in the URL-bar in the browser will show the updated location.
Note that if you have socket.io code in both pages, it will disconnect the current socket.io connection and then run the Javascript in the new page - causing it to create a new socket.io connection (if you have code in the new page to do that) as that is what happens to socket.io connections when you load and display a new web page in the browser.

Express Flash Message over multiple redirects

I have been using express-flash for a project and it has been working fine with redirects and page renders. However, I have a route /dashboard which redirects further into /dashboard/admin & /dashboard/staff as shown in the code below. Passing a message using req.flash and redirecting to /dashboard does not show up on the page. Other pages with single redirects are able to display the messages without any issue. I am guessing this problem is because of the second redirect from /dashboard to /dashboard/.*
req.flash('success_msg','Successful');
res.redirect("/dashboard");
in router.js:
app.get('/dashboard', (req, res) => {
if (req.user.role === "ADMIN") {
res.redirect("/dashboard/admin");
}
if (req.user.role === "STAFF") {
res.redirect("/dashboard/staff");
}
})
Is there a way to work around this issue, adding any statement in my router file to forward messages further into the redirects?
The flash message with value Successful is only available inside the req object in the middleware that handles the /dashboard endpoint. If you want to further redirect, you have to assign again the flash message to the next middleware.
app.get('/dashboard', (req, res) => {
// If there is any flash message with key success_msg,
// give it to the next middleware
if (res.locals.success_msg) {
req.flash('success_msg', res.locals.success_msg)
}
if (req.user.role === "ADMIN") {
res.redirect("/dashboard/admin");
}
if (req.user.role === "STAFF") {
res.redirect("/dashboard/staff");
}
})

nodeJs express petitions from ajax don't show error's page

I have a configuration to serve a website with an error handler. It works fine with app.post requests, but it does not work with ajax petitions.
Here is my error handling middleware with an example of bad path error that works correctly:
//Bad path error
app.get('*', function(req, res, next){
next('404'); //This works fine
});
//Errors control Middleware
app.use(function (err, req, res, next) {
if (typeof (err) === 'string') {
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', err), {lang});
} else {
log.error(err);
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', '500'), {lang});
}
});
when I navigate to a wrong url (some url that isn't defined on my server) it goes correctly to the defined error page (404, that is an .hbs). The problem is that this method doesn't seem to work with ajax petition like the next one:
jQuery.ajax({
type: 'POST', //I have the same problem with 'GET'
url: '/componentName/methodName',
data: {
accessToken: localStorage.token
},
}).then(function success(data) {
//TODO
}, function error(err) {
console.log(err);
})
This piece of code is for server side:
app.post("/componentName/methodName", function (req, res, next) {
var token = req.body.accessToken;
var decodedToken = jwt.decode(token);
var user_id = decodedToken.payload.user_id;
model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}, function () {
next('500'); //This broke the page and doesn't show error page
});
});
What could be the problem for not showing the error from ajax? Is my syntax correct?
new: The problem must be on the ajax success, because if I change the 'post' by 'get' in ajax and the app.get route I still having the same problem, but if I call the method directly from URL (not ajax) it works. Any idea?
new2: If I put this code on success:
jQuery('body').html(data);
it shows the error page after a few seconds. I need to do this automatically (and without those fews seconds) when any error is trhowing from the server, because I don't know if it will be ok or if is an error and the lag seconds is a problem too. Probaly anything on the server could be wrong? and it inject the error page inside the older page, so isn't a solution
model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}).catch(function () {
next('500');
})
Use catch block

Express 4.x redirect

I need some help understanding how this redirect works (and why isn't doing what I think it's supposed to do):
Problem:
I have an HTML page with a "logout" button that triggers a jQuery snippet:
$.ajax({
type: "POST",
url: "https://localhost:8000/chat",
data: {logout_msg: "get_out"},
success: (data, textStatus, jqXHR) => {
},
dataType:"json"
});
The button works, I catch the "POST" in my NodeJS server, here:
app.post('/chat', (req, res) => {
req.session.destroy((error) => {
if (!error) {
res.redirect("/login");
} else {
console.log(error);
}
});
The response is received by the browser:
But there is no web-page redirect. The browser won't load the other redirect page. I tried this in Firefox & Chrome. Both behave the same way, so I assume it's something in my code. What am I missing? Thanks.
You only "redirect" the request of jquery. If you want to open an other page in the Browser, then send the logout request with an HTML form or use location.href = '...' in the success handler.
You do not need JQuery to do simple log out.
Just add in HTML
<form action="/logout" method="post">
And in server side
app.post('/logout', (req, res) => {
req.session.destroy((error) => {
if (!error) {
res.redirect("/login");
} else {
console.log(error);
}
});
Hope this helps.

Why this not working nothing to explain more?

When I open two tabs in session after logout in one tab when I try to change data of another tab without reload page at a time redirect not working console shows the message and after that API call.
app.use(function(request, res, next) {
console.log(req.session.user);
if (req.session.user) {
next();
} else {
console.log("index.html")
res.redirect("/");
//res.end ();
}
});

Resources