Express 4.x redirect - node.js

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.

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 force refresh client webpage

Im trying to refresh a clients webpage (using a router) and everywhere I look I see something along the lines of using res.redirect(same page link of some sort), however for some reason this isnt working for me, do you know of any alternatives?
my code looks something like this
router.post('/sendSnippet', function (req, res) {
req.on('data', function(data) {
User.findOne({email: req.user.email}).then((userToEdit) =>{
if(userToEdit){
var newSnippet = {
"types":[],
"code": data.toString()
}
userToEdit.snippets.push(newSnippet)
userToEdit.save().then(()=>{
//refresh here
res.redirect('/profile/');
})
}
})
})
});
thanks for any help in advance
Assuming you are trying to force-reload a client's browser tab for your website, I don't think you can do that server-side.
You can use meta http-equiv or the Refresh HTTP header to tell the client's browser to refresh the page after some time or use client javascript to refresh the page:
Router:
router.post("/example", (req, res) => {
res.header("Refresh", "10"); // tells the browser to refresh the page after 10 seconds
res.send("your data");
});
Meta:
<head>
<!-- Refresh after 10 seconds -->
<meta http-equiv="refresh" content="10">
</head>
Javascript + html:
<html>
<body>
<script>
// reloads after 10 seconds
setTimeout(() => {
location.reload();
}, 10000);
// or you could have some kind of API to tell when to refresh the page
function check() {
const x = new XMLHttpRequest();
x.open("GET", "some path");
x.send();
x.onload = function() {
if (x.response === "done") {
location.reload();
} else {
setTimeout(check, 1000);
}
}
}
check();
</script>
</body>
</html>

res.sendFile() not sending file (no errors)

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

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

Redirecting to route from Node.JS server doesn't render the next page

Hi I have a react component that renders a form like this:
<form onSubmit={this.onSubmit}>
<!-- a bunch of inputs here -->
</form>
where the function onSubmit() makes a post request to /results using axios:
handleSubmit(e) {
var self = this;
e.preventDefault();
const {value1, value 2, ....} = this.state;
axios.post('/results', {
key1 : value1,
key2 : value2,
etc.
}).then(function(response) {
if (errors) {
self.setState({errors: response.data.errorMessage});
}
}).catch(function(error){
console.log(error);
});
}
I have a route handler for the post request in my server.js which inserts the data from the form into a database. If there are errors then it'll send that data back to the client, otherwise it should redirect to the results page. The handler looks like this:
app.post('/results', function(req, res, next) {
const reportExists = Report.findOne({
attributes: ['caseId'],
where: {caseId : req.body.caseId},
}).then(report => {
if (report) {
console.log("report already exists");
res.status(200).send({errorMessage : "Report has been submitted for this case id"});
} else {
const report = Report.create(
{
// data from form
}
).then(() => {
console.log('Record inserted successfully');
var caseId = req.body.caseId;
res.redirect("/results/" + caseId);
next();
})
.catch(err => {
console.log('failed to insert record');
res.status(200).send({errorMessage: "Failed to insert record"});
});
}
});
});
I have another app.get('/results/:caseId') handler which should render the appropriate route for the results page. But when the record is inserted successfully it doesn't redirect to that page, it stays on the same page as the form. My question is, should I be redirecting to that page from the client or the server?
Submitting an Ajax call via client-side Javascript just gets the response back from the server, whatever it is. A redirect is not automatically processed by the browser, it's just the ajax response to your javascript. It is up to your client-side Javascript to decide what to do with the redirect response.
You have a couple choices. You could detect the redirect response in your client-side Javascript and then set window.location with the new location and manually tell the browser to go to the new page. Or, you could let the browser submit the form rather than your client-side Javascript and then the browser will follow the redirect response automatically.
Also, you should not be calling next() after you call res.redirect(). Once you've sent the response, you should not enable other route handlers to execute.

Resources