Simple redirect in Express with static file - javascript

I am new to Node and Express.
I've got a static html page where the users posts his username via ajax to my server. Then I want to redirect him to another html file.
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + "/public/arena.html"));
app.get('/',function(req,res){
res.sendFile(__dirname + "/public/index.html");
});
app.post('/login',function(req,res){
var username=req.body.username;
console.log("User name = "+username);
res.redirect(__dirname + "/public/arena.html");
});
var server = app.listen(3000);
I get the username and also the respond in the browser but the server does not redirect me to arena.html. I also don't get any errors.
Why are these "easy" things so difficult in Node?
Thank you guys so much for your help.

The problem in this case is that it looks like you had some test (debugging?) code inserted into your POST route that is stopping the redirect call from running.
Here's the modified (corrected) version of your program, which will redirect the user in the way you want:
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + "/public/arena.html"));
app.get('/', function(req, res) {
res.sendFile(__dirname + "/public/index.html");
});
app.get('/arena', function(req, res) {
res.sendFile(__dirname + "/public/arena.html");
});
app.post('/login', function(req, res) {
var username = req.body.username;
console.log("User name = " + username);
// Note how I'm redirecting the user to the /arena URL.
// When you issue a redirect, you MUST redirect the user
// to a webpage on your site. You can't redirect them to
// a file you have on your disk.
res.redirect("/arena");
});
app.listen(3000);
I had to do a couple of things to get this working:
Get rid of your call to res.end. Whenever you call res.end, it will END the request, so any code that happens AFTER that call in the route will not run.
I had to create a new route for /arena. This just renders the arena.html file that you've created. This is required if you want to 'redirect' the user to an arena page.
I had to update your redirect code to actually redirect the user to /arena (the new route I created in step 2), so that the user will then hit your /arena route, and finally get back the template you are trying to show them.

Your res.redirect function is never executed because you are returning from function right before that statement.

You pass a URL to res.redirect(). That URL should be a URL that you have an appropriate route for that will serve the desired file.
Instead you are doing:
res.redirect(__dirname + "/public/arena.html");
But, that isn't a URL at all. That's a path name on your local hard disk. res.redirect() sends a URL back to the browser and, if the browser is following redirects, it will then request that URL from scratch as a branch new request. So, you need to send a URL (not a path) and you need to send a URL that you have a route configured for that will serve the desired file.
It also looks like your express.static() statements might be incorrect. For us to help more specifically with those, we need to know where the static HTML files are on your hard drive relative to __dirname and we need to know exactly how you want the URLs for them to work. For example, do you want a request for /arena.html to serve __dirname + /public/arena.html? Is that what you are trying to do? Please explain that part so we can advise more specifically on your express.static() statements.
If that is the case, then you can change your redirect to:
res.redirect("/arena.html");

Related

NodeJS middleware calling order

Why res.send("main page 2") not overriding res.send("main page 1") when I request localhost:3000 ?
While execucing this code in NodeJS only console.log("midddleware") is calling from app.use method but not res.send. I wonder why it works like that.
const express = require('express')
const app = express()
app.get('/', function(req, res){
res.send("main page 1")
})
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware")
})
app.listen(3000)
You are likely being tricked by the second request (for the favicon.ico) that the browser sends to your server.
Examining your code in more detail, here's what it will do:
const express = require('express')
const app = express()
app.get('/', function(req, res){
res.send("main page 1")
})
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware")
})
app.listen(3000)
If you do a GET request for / to your server, Express matches routes in the order declared so the first one that will match is the app.get(). It will send the response with res.send("main page 1") and because it does NOT call next(), all routing will be done and the app.use("/", ...) is never hit.
But, if you typed http://localhost:3000 into the browser, that is not the only request that the browser will send to your server. The browser will also send a request for http://localhost:3000/favicon.ico (the web site icon the browser likes to display).
That request will not be matched by the app.get("/", ...), but because app.use() accepts partial matches (app.get() requires full matches only), the /favicon.ico request will be matched by app.use("/", ..) and you will see your console.log("middleware"). You won't see the results of res.send("main page 2") because when the browser requested the favicon and got back some plain text, it will just ignore that is it clearly isn't the icon it was looking for.
If you modify your middleware to log the actual URL that is being requested, then all should be clear:
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware", req.originalUrl); // log the actual URL
})
See Middleware callback function examples. It shows this example:
var router = express.Router()
router.get('/', function (req, res, next) {
next()
})
If you call next() in your first handler, the next one will get executed. But watch out – you can only call res.send once in a request-response cycle. The second one would throw an error – because the response was already sent.
See also Writing Middleware for more examples:
The order of middleware loading is important: middleware functions that are loaded first are also executed first.
If myLogger is loaded after the route to the root path, the request never reaches it and the app doesn’t print “LOGGED”, because the route handler of the root path terminates the request-response cycle.
The middleware function myLogger simply prints a message, then passes on the request to the next middleware function in the stack by calling the next() function.

Node.js pass value from one route to another route

i have one url that is rendering data and url contains some parameter. so when in that same route i can use those parameter but i cant use it in another routes. so can someone help me with how to transfer data from one routes to another.
router.get('/token/:tokenvalue', function(req, res, next){
var token = req.params.tokenvalue;
// globalVariable.token = token;
// console.log(globalVariable.token);
req.token = token;
res.render('candidate.ejs');
})
after showing this page i am using google login so i cant store this token to req variable or somewhere else. so can someone suggest me how to resolve this issue.
There is a NPM package build for that called 'connect-flash'
$ npm install connect-flash
Than in your app.js || server.js ( your main file which boots your server )
var express = require('express');
var flash = require('connect-flash');
var app = express();
app.use(flash());
app.get('/login', function(req, res){
// Set a flash message by passing the key, followed by the value, to req.flash().
req.flash('username', 'Gaurav Gupta')
res.redirect('/profile');
});
app.get('/profile', function(req, res){
// Get an array of flash messages by passing the key to req.flash()
let message = req.flash('username')
res.render('index', { message: message }); // or {message} only es6 feature
});
The flash is a special area of the session used for storing messages. Messages are written to the flash and cleared after being displayed to the user. The flash is typically used in combination with redirects, ensuring that the message is available to the next page that is to be rendered.

retain query string after Stormpath authentication and authorization in node express site

I have a node express app , using express-stormpath for authentication/authorization
I have a GET route which is called with certain jquery parameters.
If the user is logged in everything is working as expected.
If not the user login screen is shown.
After stormpath authentication and authorization is done my query params are lost.
Is there any way to retain those?
app.get('/myRoute', stormpath.groupsRequired(['admin']), function(req, res){
console.log('req.query ',req.query);
//do somehting with the query data
res.sendStatus(200);
});
after authentication req.query is {}.
Any ideas?
Thank you for the question, I work at Stormpath and I'm more than happy to help. Our express-stormpath library is open source, and we're always happy to fix bugs and review pull requests.
Can you tell me which version of our library you are using? At the moment I'm not able to reproduce the problem you are seeing. Here is a quick example that I put together with the latest version, 3.0.1:
'use strict';
var express = require('express');
var stormpath = require('express-stormpath');
var app = express();
var port = process.env.PORT || 3000;
app.use(stormpath.init(app));
app.get('/admins', stormpath.groupsRequired(['admins']), function(req, res){
res.json(req.query);
});
app.on('stormpath.ready',function () {
console.log('Stormpath Ready');
});
app.listen(port, function () {
console.log('Server listening on http://localhost:' + port);
});
With this example, I do the following:
1.) Assert that I'm not logged in, by deleting all my cookies for localhost.
2.) Type /admin?foo=bar into the URL bar.
3.) I am redirected to the login page.
4.) I login with valid credentials.
5.) I am redirected to /admins?foo=bar, as expected, and I see the req.query object in the body of the page that is rendered. This is only true if the user is in the admins group, if they are not I will see the "Unauthorized" error message page.
Can you compare my steps and my example to your application, and let us know if there are any differences? Thanks!
I don't think that stormpath is removing query from request.
But we can check it by adding middlewhare before stormpath initialization:
var express = require('express');
var stormpath = require('express-stormpath');
var app = express();
// binding middleware to assign req.query to req.q param
app.use(function(req, res, next) {
req.QUERY = req.query;
next();
});
function restoreQuery(req, res, next) {
req.query = req.QUERY;
next();
}
app.use(stormpath.init(app, {
// Optional configuration options.
}));
app.get('/myRoute',
stormpath.groupsRequired(['admin']),
restoreQuery,
function(req, res){
console.log('req.query ',req.query);
//do somehting with the query data
res.sendStatus(200);
});

Sessions won't save in Node.js without req.session.save()

I'm building a website using Node.js, Express, and Redis for session management. For whatever reason, if I have a session variable (isLoggedIn in this example), and I refresh the page, the variable doesn't get saved, however, if I call req.session.save() after setting the variable, it does get saved to Redis (redis-cli monitor shows this - not calling save() shows that the variable isn't there, whereas calling save() shows it).
I'm using this to set up and start the server:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var swig = require('swig');
var session = require('express-session')
var RedisStore = require('connect-redis')(session);
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// Configure the favicon first. This avoids other middleware from processing the request if we know the request is for the favicon.
app.use(favicon(__dirname + '/public/images/favicon.ico'));
// All other requests will require everything else.
// Set up the view engine.
app.set('view engine', 'html');
app.set('views', path.join(__dirname, '/views'));
app.engine('html', swig.renderFile);
// Set up our logger.
app.use(logger('dev'));
// Set up JSON parsing.
app.use(bodyParser.json());
// Set up encoded URL parsing.
app.use(bodyParser.urlencoded());
// Set up the cookie parser.
app.use(cookieParser('thedogsleepsatnight'));
// Set up our session store. This will use Redis as our session management.
app.use(session({
resave: true,
saveUninitialized: true,
secret: "thedogsleepsatnight",
store: new RedisStore()
}));
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
And then, in that route, I have:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
console.log(req.session.isLoggedIn);
if (req.session.isLoggedIn) {
console.log("Logged in!");
} else {
console.log("Not logged in");
}
res.render('index');
});
router.post('/login', function(req, res) {
console.log("Going to set isLoggedIn. Currently: " + req.session.isLoggedIn);
req.session.isLoggedIn = true;
console.log("Set isLoggedIn. Currently: " + req.session.isLoggedIn);
});
module.exports = router;
From that, I should be able to navigate to /login, have the session set isLoggedIn to true, and that should save automatically to Redis. After that, heading to / should tell me that I'm logged in. Loading /login does set the variable, the second log shows that, but loading / says that I'm not logged in. redis-cli monitor shows
1414076171.241836 "setex" "sess:FIDJ9qDbX_0u9pzlC6VZEW76zZcyiPME" "86400" "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"}}"
upon saving, which doesn't include the isLoggedIn variable, but adding in req.session.save() shows:
1414076475.601644 "setex" "sess:FIDJ9qDbX_0u9pzlC6VZEW76zZcyiPME" "86400" "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"isLoggedIn\":true}"
Any idea on why I have to call req.session.save() when all of the examples I've seen don't use it?
Okay, so, I've figured it out. I'll put the answer here for anyone else that happens to get caught on this.
For GET requests, the server assumes that you're going to be sending data back, and will automatically save session data once the route is fully processed.
For POST requests (what I'm using), however, the same assumption isn't made. Session states are only saved in one of two conditions - either when data is being sent out (through res.send, res.redirect, etc.), or if you manually call req.session.save(). I was already calling /login from an AJAX request, I just wasn't returning anything if certain conditions were met. Having the server respond to the client with some data after setting the session variable fixed this.
If the brilliant answer from #thatWiseGuy is not solving the problem ... here is a hint.
I called a axios POST request from document loading:
document.addEventListener('DOMContentLoaded', function () {
axios.post('/do-magic', {
data: data
}, {withCredentials: true, credentials: 'include'}).then(res => {
// even more magic
}).catch(err => console.error(err));
}, false);
On server side, in the "do-magic" route, whenever I set a session and hit save, was not saved with the code below.
req.session.isMagic = true;
req.session.save();
I am not sure what is the reason behind, but when I invoked this call delayed, it was working properly and saving my session.
document.addEventListener('DOMContentLoaded', function () {
window.setTimeout(() => {
axios.post('/do-magic', {data: data}, {withCredentials: true, credentials: 'include'}).then(res => {
// even more magic
}).catch(err => console.error(err));
}, false);
}, 1000);

Express hanging in Chrome post AJAX delete

When issuing a same-origin AJAX delete via jQuery, subsequent calls to the server via the same client are hanging. I've excerpted the problem below. Please advise if something is malformed as this would seem like such a strange bug.
Using Express 3.5.1, jQuery 1.11.0, and Chrome 33.x.
To replicate:
Create directory with code below and install Express dependency
node main.js
Visit localhost:5000 in Chrome 33
Click on simulated deletion link
Wait for a moment, and refresh
In the Network region, Chrome will handle the DELETE request ok, but the subsequent server call will stay as "pending". I have tried both HTML and JSON dataTypes and responses.
main.js:
// Dependencies
var http = require('http'),
express = require('express');
// Basic app
var app = express();
// Logging (for debugging), and parsing dependencies
app.use(express.logger());
app.use(express.json());
app.use(express.urlencoded());
app.use(express.cookieParser());
app.use(express.methodOverride());
// Simple home page
app.get("/", function(req, res) {
var head = "<head><script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js'></script></head>",
body = "<a id='link' style='text-decoration:underline;cursor:pointer;'>Click here to simulate delete</a>, wait a second, then try refreshing.",
params = {
url : "/item/5",
type : "DELETE"
}
ajax = "<script>$(function() { $('#link').click(function() { $.ajax(" + JSON.stringify(params) + "); }); });</script>"
res.send("<html>" + head + "<body>" + body + ajax + "</body></html>");
});
// Simulated deletion
app.del("/item/:id", function(req, res) {
res.send(204);
});
// Make the server listen
var server = http.createServer(app).listen(5000);
This ended up being a non-node-related third-party plugin (I believe it was Sophos, but I may be mistaken). Once the plugin was disabled, everything worked fine.
You can see more information here: https://github.com/strongloop/express/issues/2069

Categories