NodeJS Express Routing - Only first request gets rendered - node.js

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.

Related

Loading while serving index.html with nodeJS

I have a react project with this nodeJS server:
(basically catch the request and modify some meta-tags for the blog post data , not something huge):
// here we serve the index.html page with the meta tags
app.get("/blog/post/:id", async (req, res, next) => {
//get post info
const postId = req.params.id;
const { postTitle, postDesc } = await fetchBlogPost(postId);
fs.readFile(indexPath, "utf8", (err, htmlData) => {
if (err) {
console.error("Error during file reading", err);
return res.status(404).end();
}
if (postTitle && postDesc) {
htmlData = htmlData
.replace(
"<title>my title</title>",
`<title>${postTitle}</title>`
)
.replace("__META_OG_TITLE__", postTitle)
.replace("__META_OG_DESCRIPTION__", postDesc)
.replace("__META_DESCRIPTION__", postDesc);
return res.send(htmlData);
}
});
});
However - when the user is reaching /blog/post/ID , the server runs and fetch the post data while using the await method - which cause the user to see a blank white page for 2-3 seconds untill the server returns the html.
Is there anything that i can do about it ? i would like the user maybe to see some loading before, because right now its a blank white page.

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

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.

app.render() not displaying in browser

I'm trying to use app.render() to display a jade file in the browser. In the following code, the html is displayed to the console correctly, but the browser never shows the related file.
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
});
EDIT:
I have this handler:
app.get('/unavailable', display.unavailable);
Then beneath this code in the same file (app.js) I have this:
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
else
conn = sqlconn; //save the sql connection globally for all client's to use
});
So, what I want to happen is when the err happens with the SQL connection, the /unavailable handler is executed and a static html page is displayed that says the service is down. However, because the error occurs on the server, and not the client, I don't have access to a response object at that time. I'm trying to artifically manufacture the client 'redirecting' to /unavailable in their browser to see the message.
Obviously you don't send the html to the browser. Use res.render inside a route without callback, i.e.
res.render('unavailable', {title: 'Unavailable'});
or send the result of rendering like here:
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
res.send(html);
});
Read more about the difference here:
What's the difference between "app.render" and "res.render" in express.js?
save a global var sqlOK = false, set it in sql.open callback, and redirect to /unavailable if you get a request while sqlOK is not true. you were also missing brackets around the else statement.
var sqlOK = false;
app.get('/unavailable', display.unavailable);
app.get('*', function(req, res, next){
if(!sqlOK){
return res.redirect('/unavailable');
//return res.send(500)
};
next();
});
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
} else {
conn = sqlconn; //save the sql connection globally for all client's to use
sqlOK = true
}
});

Response headers of previous request affecting current request

The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.

Resources