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.
Related
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.
The Problem
I deployed a create-react-app webapp to aws ec2. It's used to display data from a database and send data to it. I use ExpressJS, CORS and MySQL.
With the following code i fetch the corresponding URL and the server.js sends back the database content. Until here, everything works fine.
getBets = _ => {
fetch("http://ec2***.amazonaws.com
.then(response => response.json())
.then(response => this.setState({bets: response.data}))
.catch(err => console.error(err))
};
The problem begins when sending data to the database with the following code:
addBet = _ => {
const { bet } = this.state;
fetch(`http://ec2***.amazonaws.com/bets/add?name=${bet.person_name}&bet=${bet.time_bet}`)
.then(response => response.json())
.then(this.getBets)
.catch(err => console.error(err))
};
On click the addBet-function populates the db, but in chrome I following error:
GET http://ec2***.amazonaws.com/bets/add?name=Peter%20Pan5&bet=10:17%205 net::ERR_EMPTY_RESPONSE
and
TypeError: Failed to fetch
Regarding chrome dev-tools, the first error corresponds to the fetch in the addBet function and the second error to the catch part.
On the server side I've the following code for processing the fetch:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES("${name}", "${bet}", CURTIME())`;
connection.query(INSERT_BET, (err, res) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I want to mention, that the res paramter in the app.get part is unused. That tells me my IDE.
After a lot of hours digging deeper in the topics of expressJS and the fetch api, I guess, that the app.get part doesn't send a response to the server. But the fetch need some response.
My Question
How do I have to change the code in the app.get part to send a proper response back to the server?
AND
Am I right with my guess?
In MYSQL when you do an insert query you get back err,results and fields in the callback function like this:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error,
results, fields) {
if (error) throw error;
console.log(results.insertId);
});
You have used the parameter res for result and then you have used res.send() which now corresponds to that res parameter in the callback function and not the res object.Rewrite it like this:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES(?,?,?)`;
connection.query(INSERT_BET,[name,bet,CURTIME()] ,(err, result) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I have also used prepared statement in place of normal sql queries. These are used to prevent sql injections. I hope it will work now.
I'm currently designing a oauth login system and I've encountered the following problem. I'm trying to redirect users back to the homepage once they have been logged in and session data has been set, but the res.redirect('/') throws NodeError: Cannot set headers after they are sent to the client. I cannot seem to get it to work. Below is the code which is causing the fault:
app.post(
"/auth/openid/return",
passport.authenticate("azuread-openidconnect", {
failureRedirect: "/login"
}),
function(req, res) {
let userProperties = req.user;
new Promise((resolve, reject) => {
MongoClient.connect(url, function(err, db) {
if (err) next(err);
let dbo = db.db("Lektier");
let query = {
oid: req.user.oid
};
dbo.collection("users").findOne(query, function(err, result) {
db.close();
if (result) {
let type = result.type;
resolve(type);
}
});
});
}).then(type => {
req.session.regenerate(function() {
req.session.user = userProperties.upn;
if (type == "teacher") {
req.session.teacheruser = userProperties.upn;
}
let names = userProperties.displayName.toString().split(" ");
req.session.FirstName = names[0];
req.session.LastName = names[1];
res.redirect("/");
});
});
}
);
Some help on the matter would be appreciated.
Whenever you see a message like this Cannot set headers after they are sent to the client, it means that the logic of your endpoint tried to send a response to the client and failed, because it actually already responded. res.redirect() of express actually sends some 3xx http status to the client and if you are saying that this method throws the error you're facing, something before it already sent a response.
I didn't find anything that could respond in the snippet you provided (besides the very res.redirect()), so I suggest you to look into your middleware. For example into passport authentication, since it is mentioned here.
I'm trying to download a photo through a URL passed as a query string using Express, but every time I try to use it, I get Error: Invalid URI "favicon.ico" Is there a way I can get my browser to stop requesting a favicon? For downloading images, I'm using the image-downloader package (NPM page)
Code:
app.get('/:url', (req, res) => {
let url = req.params.url;
const options = {
url: url,
dest: /path'
};
download.image(options)
.then(({ filename, image }) => {
console.log('File saved to ', filename);
})
.catch((err) => {
console.log(err);
});
res.send("Done");
});
It's probably easiest to just make a route for favicon.ico in your server.
app.get('/favico.ico', (req, res) => {
res.sendStatus(404);
});
Of course, you could actually send a valid icon too if you wanted, but this will at least keep your Express server from showing an error.
FYI, this has nothing to do with the image-downloader. This has to do with the browser requesting a favico.ico icon that it uses to show in the URL bar (and some other places in the browser UI). If your server returns a 404 for favicon.ico, the browser will use a generic icon in its UI.
If you want to make yourself a simple favico.ico, you can go here and it will help you generate one and then you can change the above route to:
app.get('/favico.ico', (req, res) => {
res.sendFile("myfavico.ico");
});
Try using another package like request module. I believe it got this type of things handled.
var fs = require('fs'),
request = require('request');
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
});
};
download('https://www.google.com/images/srpr/logo3w.png', 'google.png', function(){
console.log('done');
});
When I hit my api I want to redirect my url from https://myapp.herokuapp.com/token/aaa.bbb.ccc to https://myapp.herokuapp.com/messages/:id. I also want to render my message view
Code:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) res.json({error: err})
res.render('message', {message})
})
})
Here, I successfully render my message view but the URL for the below api is still https://myapp.herokuapp.com/token/aaa.bbb.ccc and not https://myapp.herokuapp.com/messages/:id
Another attempt:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) res.json({error: err})
res.redirect('/messages/'+message._id)
})
})
Now, the URL is https://myapp.herokuapp.com/messages/:id but the message view is not rendered. A JSON is rendered that displays the message
How do I redirect to https://myapp.herokuapp.com/messages/:id and also render the message view?
You should first redirect:
app.get('/token/:id' , (req, res) => {
var decoded = jwt.verify(req.params.id, 'blabla');
Message.findById(decoded.messageId, (err, message) => {
if (err) return res.json({error: err}); // see #partycoder's answer
res.redirect('/messages/'+message._id)
})
})
Next, you need to adjust the route handler for /messages/:id. Right now, it sounds like it's only used for XHR requests, so it will always return JSON. You can add a check to see if the request is an XHR-request or not, and either return JSON (for XHR) or a rendered template (for non-XHR):
app.get('/messages/:id', (req, res) => {
...
if (req.xhr) {
return res.json(...);
} else {
return res.render(...);
}
});
(documentation for req.xhr, be aware that the method on which this is based is not foolproof)
However, perhaps it's better to use content negotiation, where the client explicitly tells your server what format the response should be. The upside of this is that it's much more explicit, the downside is that you may have to change some client-side code. Documentation here: http://expressjs.com/en/4x/api.html#res.format