Why am i getting the output twice in the Node console? - javascript

I have just started learning NodeJS, Kindly help me understand this
const http = require('http');
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('In the Middleware');
next();
});
app.use((req, res, next) => {
console.log('In another Middleware');
res.send('<h1>Hello From Express</h1>');
});
app.listen(3000);
Output is
In the Middleware
In another Middleware
In the Middleware
In another Middleware
Expected
In the Middleware
In another Middleware

Browsers will often make multiple requests to a server. For example, they will usually try to fetch a favicon.ico file. Another example is a CORS preflight request.
If you open the developer tools in your browser, go to the Network tab, and then refresh your page, you will see all the requests the browser makes (you may need to check the "Disable cache" checkbox first). If you see more than one request, that's probably the reason you're seeing your text logged multiple times.
Another simple way to test this is to try making a request to your server from a client other than your browser. For example, run this from the command line: curl http://localhost:3000. That command should make only a single request to your server. Do you see your text output multiple times in that case? If not, then that's further evidence your browser is making multiple requests to your server.

I would say that this happens because the browser usually makes a request for the favicon.ico as well, but you can check in the Network tab of the developer tools.

In my case, i was using:
app.use('/',function(req,res){
console.log('here');
})
So, I though whenever I refresh my frontend this should run once. But, browser actually sends multiple request whenever you refresh. (i.e favicon)
That's the reason your code logging multiple times.

Related

res.redirect() not working properly in express JS

I've been working on creating an API for a blockchain I've made.
Everything was going fine, until I updated Node and Windows...
====== ISSUE ======
After performing a POST request, I cannot redirect to a GET page anymore. (Using Postman only no front end yet).
For some reason, it was working fine and now I can't do it in a simple test file even.
I've attached the simple express test file below (which duplicates my problem).
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Hello World from GET!");
});
// BELOW IS THE WAY I'VE WORKED AROUND IT FOR NOW
// app.post("/", (req, res) => {
// res.send("Hello World from POST!");
// });
app.post("/me", function (req, res) {
res.redirect("/"); // ISSUE HAPPENS HERE
});
var server = app.listen(8081, () => {
var port = server.address().port;
console.log(`Example app listening at http://localhost:${port}`);
});
Using PostMan, now whenever I try to redirect after app.post("/me"), it will return a 404 error shown below...
<body>
<pre>Cannot POST /</pre>
</body>
I found out if I add a POST method to the "/" route (commented out above), it will work correctly.
Why didn't I have to do this before??? Did I break my computer? lol
I have tried uninstalling Node v19 and reinstalling Node v16. (no change)
I uninstalled the Windows updates that I did when restarting for Node v19 installation.
Your browser makes a POST / request after redirection, which your app does not accept, it expects a GET / request instead.
Whether a redirection after a POST request leads to a GET request or another POST request depends on the HTTP status (between 301 and 308) as well as on the browser. Perhaps the Windows update changed the default behavior of your browser?
To avoid any ambiguity, you should use
app.post("/me", function (req, res) {
res.redirect(303, "/");
});
See also Google Apps Script return 405 for POST request.
I deactivated "Follow original HTTP Method" in PostMan request settings (near body and headers).
This solved my problem. PostMan was executing another POST request on my redirect, instead of a GET.
Following the link Heiko provided above, and another link on that page about changing PostMan settings, led me to this page...
https://makolyte.com/postman-follow-redirects-with-the-original-http-method-and-content-type/
I was able to disable the "Follow original HTTP Method" which will "Redirect with the original HTTP method instead of the default behavior of redirecting with GET" while it is activated.
It appears this is a per request change in PostMan however.
Not sure exactly what caused this to happen, but it works!
I also found out about the PostMan console, located at the bottom of the screen. This was helpful in debugging the issue.

Clustered Nodejs server and CAS server: CORS issue

I have a Nodejs based server, which uses a middleware which basically redirects the user to a CAS to manage authentication. The CAS server responds with a ticket, and finally my Nodejs server trades the ticket for an user object with the CAS and stores it in a session.
This process works perfectly fine without cluster.
Today i wanted to clusterize my Nodejs server, using https://nodejs.org/api/cluster.html (thing that i've already done without any problem).
So instead of having:
let server = http.createServer(app);
server.listen(PORT, HOST, () => {
// Keep a track that the server has been launched
logger.log(`Server running at http://${HOST}:${PORT}/`);
});
Where everything was working fine, I now have:
if(cluster.isMaster) {
// This is the mother process
// Let's create the threads
for(let i=0; i < NB_WORKERS; i++) {
cluster.fork();
}
// When a child crash... Restart it
cluster.on('exit', (worker, code, signal) => {
logger.log("info", "A child died (PID: %s). It was killed by code or signal %s. Restarting...", worker.process.pid, dode || signal);
cluster.fork();
});
} else {
// This is a child
// Create the server, based on http
let server = http.createServer(app);
server.listen(PORT, HOST, () => {
// Keep a track that the server has been launched
logger.log(`Server running at http://${HOST}:${PORT}/`);
});
}
When i launch the server, it actually starts the server on NB_WORKERS threads, as expected. But when i want to access the app delivered by my Node server with my browser, i have the following error:
which says if you can't see:
XMLHttpRequest cannot load https://localhost:8443/cas/login?
service=http://localhost:3000. No 'Access-Control-Allow-Origin' header is
present on the requested resource. Origin 'http://localhost:3000' is
therefore not allowed access
https://localhost:8443 is were my CAS server is running, and http://localhost:3000 is where my Node server is running.
Note that if i set NB_WORKERS to 1, everything works fine again.
I understand that setting 'Access-Control-Allow-Origin' header in my CAS server config would probably make everything works fine, but i don't understand why it's working with one thread and not with two or more.
What am i missing ?
I finally managed to make it work, so i post here in case someone come across a similar issue.
About Node session
As I said, my Nodejs server stores datas into a session. In this case, it was a simple express-session with the default MemoryStore since i'm still in development.
When clustering, express-session default store is NOT shared between threads. This means that requests supposed identified by the session were sometimes not, depending of which thread handled the request. This caused the authentication middleware to ask the CAS server again.
To make it work, i had to use a persistant store for my session, such as Redis.
About the CORS issue
I'm not really sure about what caused this strange issue, but here what i thought about:
Since my CAS server uses HTTPS protocol, some handshakes were made between both servers due to TSL (Hello, Certificate exchange, Key exchange). Maybe these were broken if not only one thread tried to do it (One makes the Hello, then the response is sent to another thread => this can't work).
Still it's very strange, because it's very low-level, and almost none cluster Node app would work if this was not managed internally.
In order to make the previous handshakes work, i guess that each thread must be identified somehow. A likely way to do it is to assign a custom port to each thread: the process still run on port 3000, but each thread use a custom one to communicate.
Maybe this was causing my issue, but I can't really tell precisely.
So, i just had to manage correctly my session store to make it work. But please let me know if i was wrong somewhere about the CORS issue.

Keep Node JS connection opened and write (GET Request)

I was trying to implement a NODE JS get method where I could encode in the url parameters and send back responses like in Server Sent Events.
For example, when I used:
curl -D- 'http://localhost:8030/update'
The server would return a message, and then keep the connection opened to return more messages (like Push).
I was using require('connect'), I tried with require('express') but can't get it working.
Here's part of my code:
var http = require('http');
var connect = require('express');
var app = connect();
app.use(bodyParser.urlencoded({ extended: false }))
.use(bodyParser.json()) // JSON
.use(cors(corsOpts))
.get('/update', updateMiddleware);
var server = http.createServer(app);
server.listen("twserver.alunos.dcc.fc.up.pt", 8030);
function updateMiddleware(req, res) {
res.setHeader('Connection', 'keep-alive');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.writeHead(200);
setTimeout(function() {
res.write("this is an event");
res.flushHeaders();
}, 1000);
setTimeout(function() {
res.write("this is another event");
}, 2000);
// should print without ending
}
EDIT: I found it was working, but only in chrome. In terminal, I only receive it after waiting a long time, and messages come like in chunks.
You can't use a single HTTP request to listen for multiple event data. If you are really stuck with HTTP (i.e. WebSocket or WebRTC is not an option) then the technique you are looking for is called long polling. This basically works this way:
Client sends request to server
Server waits until an event happens (or until a specific but not too long timeout, so the client application does not throw a timeout error for the request)
Server responses with a complete http response, containing the details of the event
Client receives the event details and immediately sends another request to listen to further events.
This method really takes advantage of HTTP Keep-Alive
EDIT:
For me it looks like your code does not follow the protocol of server sent events. Here is a tutorial: Server-Sent Events in nodejs.
Following another tutorial on MDN about Server-Sent Events, the structure of the messages should be the following:
: this is a test stream
data: some text
data: another message
data: with two lines
Note that the data to be sent must be followed by a double new-line \n\n.
In general, http endpoints in Express aren't supposed to do things like that. If you want live event data, the best way is to use a web socket.
That being said, this thread has an example on how to force Express to do this.
socket.io or Webrtc is the best choice

Create/configure node.js dispatcher/proxy

I'm working on javascript Single Page Application with Aurelia framework and using simple fake backend(express.js) for prototyping purposes.
Backend runs on localhost:8081 and client app on localhost:9000
There are some Cross Domain issues because these are different ports, and adding cross origin headers to the backend seems cumbersome to me.
What i want is simple dispatcher/proxy that would run on, say, localhost:3000 and redirect incoming calls in this manner (localhost:3000/app => localhost:9000) (localhost:3000/api => localhost:8081) Thus eliminating cross domain issues.
I'm looking for really simple solution, maybe there is some node.js app that suited just for such cases.
If you are using Express, you can add this routes to your app.
You need to install the module 'request' for this example
// At the top
var request = require('request');
And then:
//APP
app.use('/app', function (req, res) { request('http://localhost:9000' + req.originalUrl).pipe(res); });
//API
app.use('/api', function (req, res) { request('http://localhost:8081' + req.originalUrl).pipe(res); });

Force SSL with expressjs 3

I'm running a node.js express 3 server with no proxies and using SSL.
I'm trying to figure out how to force all connections to go through https.
Google searching shows me this:
https://groups.google.com/forum/#!topic/express-js/Bm6yozgoDSY
There's currently no way to force https redirects, though that seems
like a bit of a strange work-around. We have an https-only app and we
just have a simple ~4 line node http server that redirects, nothing
fancy
Which is what I need, but he doesn't say what those 4 lines are.
How do we do this? Thanks.
I don't really understand the point in starting two servers when only one can do the job perfectly. For example, by adding a simple middleware in your server file:
app.use(function(req, res, next) {
if(!req.secure) {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
}
next();
});
This will redirect any non-secure request to the corresponding HTTPS page. For example, http://example.com/ to https://example.com/ and http://example.com/foo?bar=woo to https://example.com/foo?bar=woo. This is definitely the behavior I would expect. Maybe you should filter this by host, so it redirects only on domains for which you own and installed a proper certificate.
If your app is running behind another server like Nginx, you may want to add the configuration parameter app.set('trust proxy', true). Or, even better, make Nginx do the redirect itself, which will be more efficient than any Node.js app.
Edit: According to my benchmarks, join is a little faster than + for concatenating strings. Nothing dramatic, but every win is a win...
You should create a second server listening on 80 and redirect with a 301 header to your https server:
var express = require('express');
var app = express();
app.get('/', function(req, res, next){
res.redirect('https://' + app.address().address)
});
app.listen(80);
I had a similar problem and the redirect solution is not suitable for me because essentially I want to get rid of the browser's insecure warning,
So instead of redirect every message, I did:
app1 = new express()
app1.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/redirect.html'));
});
app1.listen(80, function(){'redirect server running on 80 port'})
and in the redirect.html is just a redirecting html file:
<meta http-equiv="refresh" content="0; URL='https://my-site.com'" />
Of course, this won't work for complicated cases when you want to redirect all routings, but for me, I only want to redirect my homepage to my https homepage and get rid of the browser's insecure warning. Hope this help somebody!

Categories