I am new to node.js and I am trying to make a simple task list app using express.js and express-session. However, for a reason that I don't understand most of the routes are called two or tree times when I make a request and it shouldn't. For instance, if I send a request to /new the new task is sometimes added two (or three) times instead of one and this causes a problem...
I read in other threads that the problem could come from the browser trying to get a favicon, however if I log all incoming request url (console.log(req.url)) on the /new route, the duplicated requests are always /new and not a favicon...
Here is my code :
var express = require('express');
var session = require('express-session');
// Create a new express application instance
var app = express();
// Initialize session
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {}
}));
// Initialize req.session.tasks if needed
app.use(function (req, res, next) {
if (req.session.tasks === undefined) {
req.session.tasks = [];
}
next();
});
app.get('/', function (req, res) {
res.send(req.session.tasks);
});
// Create a Test Task
app.get('/new', function (req, res) {
console.log(req.url);
req.session.tasks.push("Test Task");
res.redirect('/');
});
app.get('/clear', function (req, res) {
req.session.tasks = [];
res.redirect('/');
})
app.listen(3000, function () {
console.log('Task Server is listening on port 3000!');
});
Do you have a idea of what could be the cause of this problem and how to avoid it ??
Thanks a lot !
Related
EDIT: After a deal of working towards a solution I am convinced this has to do with the way the package.json file compiles a lot of the site on the fly currently. Webpack, and babble are involved. I think the solution will setting up a test server that works with a fully compiled site.
I am working my way through a node course, and I want to stop before I go any further and add testing to it.
ATM I'd just like to be able to test the home route kicks back a 200. With postman it does no problem, but I can't get mocha to test it.
app.js:
const express = require("express");
const session = require("express-session");
const mongoose = require("mongoose");
const MongoStore = require("connect-mongo")(session);
const path = require("path");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const passport = require("passport");
const { promisify } = require("es6-promisify");
const flash = require("connect-flash");
const expressValidator = require("express-validator");
const routes = require("./routes/index");
const helpers = require("./helpers");
const errorHandlers = require("./handlers/errorHandlers");
// create our Express app
const app = express();
// view engine setup
app.set("views", path.join(__dirname, "views")); // this is the folder where we keep our pug files
app.set("view engine", "pug"); // we use the engine pug, mustache or EJS work great too
// serves up static files from the public folder. Anything in public/ will just be served up as the file it is
app.use(express.static(path.join(__dirname, "public")));
// Takes the raw requests and turns them into usable properties on req.body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Exposes a bunch of methods for validating data. Used heavily on userController.validateRegister
app.use(expressValidator());
// populates req.cookies with any cookies that came along with the request
app.use(cookieParser());
// Sessions allow us to store data on visitors from request to request
// This keeps users logged in and allows us to send flash messages
app.use(
session({
secret: process.env.SECRET,
key: process.env.KEY,
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
})
);
// // Passport JS is what we use to handle our logins
app.use(passport.initialize());
app.use(passport.session());
// // The flash middleware let's us use req.flash('error', 'Shit!'), which will then pass that message to the next page the user requests
app.use(flash());
// pass variables to our templates + all requests
app.use((req, res, next) => {
res.locals.h = helpers;
res.locals.flashes = req.flash();
res.locals.user = req.user || null;
res.locals.currentPath = req.path;
next();
});
// promisify some callback based APIs
app.use((req, res, next) => {
req.login = promisify(req.login, req);
next();
});
// After allllll that above middleware, we finally handle our own routes!
app.use("/", routes);
// If that above routes didnt work, we 404 them and forward to error handler
app.use(errorHandlers.notFound);
// One of our error handlers will see if these errors are just validation errors
app.use(errorHandlers.flashValidationErrors);
// Otherwise this was a really bad error we didn't expect! Shoot eh
if (app.get("env") === "development") {
/* Development Error Handler - Prints stack trace */
app.use(errorHandlers.developmentErrors);
}
// production error handler
app.use(errorHandlers.productionErrors);
// done! we export it so we can start the site in start.js
module.exports = app;
The application is set up to run routes through a file at routes/ called index.js. That file then calls up the view file...
My test can't seem to get properly routed though.
const expect = require("expect");
const request = require("supertest");
const app = require("./../../app");
describe("Dummy Test", () => {
it("Should return 5", () => {
const result = 2 + 3;
expect(5);
});
});
describe("Get /home", () => {
it("should get home", done => {
request(app)
.get("/home")
.expect(200)
.end(done);
});
});
It always returns a 500. I can make the repo public if a deeper look might help.
Not sure if I'll solve it, but hoping this at least sparks some new debugging ideas for you. I normally use superagent with Jest to test, but this looks like a more or less similar setup.
I did some code comparison to the docs (https://www.npmjs.com/package/supertest).
In this example, there is some error handling on the .end(). Wondering if adding that might help you diagnose?
describe('POST /users', function() {
it('responds with json', function(done) {
request(app)
.post('/users')
.send({name: 'john'})
.set('Accept', 'application/json')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
Also, this example shows the done being added as a comma separated instead of a .end(done) on it's own line. Your current way is also shown, but it's just another way to try.
describe('GET /user', function() {
it('respond with json', function(done) {
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});
If none of that helps, my last thoughts are what is your '/home' route actually returning? I see the routes import in your app file, but cannot see that actual routes for reference. Have you tried additional console.log's / error handling in your /home route to examine the back-end's perspective on what is being sent?
I'm trying to share an express-session with socket.io session but am not able to access the saved session variables from my express routes. I'm using express-socket.io-session to share the session. When I save data on the socket's "connection" event, and then trigger a route change, express seems unaware of the changed session data.
const path = require('path');
const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const session = require('express-session')({
secret: 'secret',
resave: true,
saveUninitialized: true
});
var sharedsession = require('express-socket.io-session');
app.use(session);
io.use(sharedsession(session, {
autoSave: true
}))
io.on('connection', (socket) => {
socket.handshake.session.data = ['connection']
console.log(socket.handshake.session);
// First
socket.on('login', data => {
socket.handshake.session.data.push('login');
console.log(socket.handshake.session);
});
})
// Second
app.get('/route', (req, res, next) => {
console.log(req.session.data); // => undefined
})
app.get('/*', (req, res, next) => {
res.sendFile(path.join(__dirname, 'index.html'));
})
server.listen(3000);
I've spent a lot of time troubleshooting with different configuration options with no success. The best way for me to demonstrate the problem by creating an extremely minimal example in a repo:
https://github.com/matt-mcdaniel/express-socket-test
Why can't I access the saved data from my socket connection in my express routes?
I think you are missing the .save() method when you are setting a value in session from socket io like this -
socket.handshake.session.data = ['connection'];
socket.handshake.session.save();
This should work probably.
I am pretty new to NodeJS and this is my first time with Express 4, I'm trying to build a simple RESTful front-end around a command-line application. There will ultimately only be one GET and one POST necessary, with the POST handling about 3 or 4 different parameters. The GET should call the command-line application with all default parameters, which is basically just a status check and return the exit status upon completion. The POST will pass along POST parameters on the commandline. I know that this basically calls for an asynchronous call, like child_process.execFile(), but I can't seem to figure out how to actually return the response from within the callback function.
This is the tutorial I used as a starting point, omitting the mongoose dependency, because I have no need for MongoDB, so I basically just followed it up to the point where you start the server. At this point, I'm pretty lost. I always hate writing async code...
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
var child_process = require('child_process');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080; // set our port
var router = express.Router(); // get an instance of the express Router
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
router.get('/myapp/status', function(req, res) {
console.log(req.user);
child_process.execFile(
'casperjs',
['myfile.js', '--cmd="Status"', '--user="myuser"', '--pass="#mypass"'],
null,
function(response) {
// ???
}, res);
});
app.use('/api', router);
app.listen(port);
console.log('Magic happens on port ' + port);
You can try the following:
router.get('/myapp/status', function(req, res) {
console.log(req.user);
child_process.execFile(
'casperjs', //command
["myfile.js --cmd=Status --user=myuser --pass=#mypass"], // args
function(err, stdout, stderr) { //callback
if (err) {
return res.status(500).send(err);
}
res.send(stdout); // to send response to client
});
});
I ran into this while setting some middleware only for certain routes. In my application, I only need session and csrf on my contact page.
Here's the problem. When I require them outside and use the references in an array as a route parameter, it works all fine. Session works fine and expects the same cookie(sessionID) for every request and CSRF check passes as it should.
But, when I use a function to return an array of 'middleware', then it behaves abnormally. When I 'get' contact, I receive one sessionID, and when I 'post' to it, apparently the session restarts and I get an error thrown since I have the older sessionID. And weirdly the CSRF also expects another csrf token and not the one it sent me.
I get that it's something to do with the way I'm requiring in modules, but I'd love to get a clear explanation as to why this is happening. Do note that I am beginner, so go easy on me :) Thanks!
// MIDDLEWARE FOR CERTAIN ROUTES
// This works fine!
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
router.get('/contact', [session, csrf], function(req, res, next) {
});
router.post('/contact', [session, csrf], function(req, res, next) {
});
// but this does not work
var contactMiddleware = function() {
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
return [session, csrf];
};
router.get('/contact', contactMiddleware(), function(req, res, next) {
});
router.post('/contact', contactMiddleware(), function(req, res, next) {
});
Hi have you tried to do this instead :
router.get('/contact', contactMiddleware, function(req, res, next)
edit:
so you only need a static array as far as I can see, so why not just doing this
var array = (function() {
var session = require('express-session')({secret: 'i lit fire to the rain', saveUninitialized: false, resave: true});
var csrf = require('csurf')();
return [session, csrf];
})();
and then passing your array to the route?
router.get('/contact', array, function(req, res, next) {
res.send('contact');
});
I'm confused by this. Why can I see this at the url http://localhost:1337/admin/hello/holly but not at the url http://localhost:1337/admin/users/holly? It's sending text right? the res.send (sending 'hello' to the page). But surely the adminRouter.get should be pulling the 2nd url (with the word 'users' in the path) ? It's basically doing the opposite of what I 'm expecting it to do.
Here's the code snippet.
// route with parameters (http://localhost:1337/admin/users/:name)
adminRouter.get('/users/:name', function(req, res) {
res.send('hello ' + req.params.name + '!');
});
** edit: Here's the whole code with the other routes:
// load the express package and create our app
var express = require('express');
var app = express();
// send our index.html file to the user for the home page
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
// get an instance of the router
var adminRouter = express.Router();
// route middleware that will happen on every request
adminRouter.use(function(req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
// route middleware to validate :name
adminRouter.param('name', function(req, res, next, name) {
// do validation on name here
// blah blah validation
// log something so we know its working
console.log('doing name validations on ' + name);
// once validation is done save the new item in the req
req.name = name;
// go to the next thing
next();
});
// route with parameters (http://localhost:1337/admin/users/:name)
adminRouter.get('/users/:name', function(req, res) {
res.send('hello ' + req.params.name + '!');
});
// create routes for the admin section
// admin main page. the dashboard
adminRouter.get('/', function(req, res) {
res.send('I am the dashboard!');
});
// users page
adminRouter.get('/users', function(req, res) {
res.send('I show all the users!');
});
// posts page
adminRouter.get('/posts', function(req, res) {
res.send('I show all the posts!');
});
// apply the routes to our application
app.use('/admin', adminRouter);
// start the server
app.listen(1337);
console.log('1337 is the magic port!');
So the answer to this one was to just manually restart my server and the problem corrected itself.
Why it had gotten itself in a twist and was doing the reverse of what it should have been doing who knows, but on restarting the server it worked correctly.