Express 4 middleware error handler not being called - node.js

For certain pages I have custom 500, 404 and 403 error handling in my app. So for instance after an unsuccessful database query I'd go:
return next({status: 404, message: 'Record not found'});
or
return next(new Error('Bad things have happened')});
In my middleware I have an error handler:
app.use(function (err, req, res, next) {
// handle error
});
Problem is that the error handler is never called, instead the error callstack is being printed into the browser. I want the handler to render a custom error page.
app.js
var express = require('express')
, app = express()
, swig = require('swig')
, config = require('./lib/config')
, env = process.env.NODE_ENV || 'development'
, path = require('path');
config.configure(env);
app.engine('html', swig.renderFile);
app.set('view cache', false);
swig.setDefaults({
cache: config.get('swigCache')
});
app.set('view engine', 'html');
app.set('views', __dirname + '/lib/views');
require('./lib/util/swig');
require('./lib/initialisers/mongodb')();
require('./lib/initialisers/aws')();
require('./lib/middleware')(app); // first load middleware
require('./lib/routes')(app); // then routes
var server = app.listen(config.get('port'), function() {
console.info('config: ' + JSON.stringify(config.getCurrent()));
console.info('NODE_ENV: ' + env);
console.info('server running: ' + JSON.stringify(server.address()));
});
routes.js
module.exports = function(app){
app.get('/', require('./views/').index);
app.get('/blog', require('./views/blog').index);
app.get('/blog/:slug', require('./views/blog').getBySlug);
app.route('/report/:slug')
.get(require('./views/report/').index)
.post(require('./views/report/').doReport);
// Very long file with tons of routes. Simplified version.
middleware.js
var express = require('express')
, app = express()
, path = require('path')
, logger = require('morgan')
, cookieParser = require('cookie-parser')
, bodyParser = require('body-parser')
, passport = require('passport')
, session = require('express-session')
, mongoStore = require('connect-mongo')(session)
, compression = require('compression')
, favicon = require('serve-favicon')
, config = require('./config')
, flash = require('connect-flash')
, multer = require('multer')
, csrf = require('csurf');
module.exports = function(app) {
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.use(express.static(path.join(__dirname, config.get('staticContentPath')), {
maxAge: (60 * 60 * 24) * 1000
}));
app.use(session({
resave: true,
saveUninitialized: true,
secret: 'da755fc0-6882-11e4-9803-0800200c9a66',
cookie: {
maxAge: 24 * 60 * 60 * 1000 // 24 hrs
},
store: new mongoStore({
url: config.getMongoConn()
})
}));
app.use(logger('dev'));
app.use(flash());
/**
* 301 redirects
*/
app.use(function(req, res, next) {
var host = req.get('host');
// AWS IP --> http
if (host == 'xx.xxx.xxx.xxx') {
return res.redirect(301, config.get('url') + req.originalUrl);
}
// AWS origin --> http
if(host == 'xxx-xxx-xxx-xxx-xxx.ap-southeast-2.compute.amazonaws.com'){
return res.redirect(301, config.get('url') + req.originalUrl);
}
// www --> http
if (/^www\./.test(host)) {
host = host.substring(4, host.length);
return res.redirect(301, req.protocol + '://' + host + req.originalUrl);
}
// Trailing slash --> http
if (req.path.substr(-1) == '/' && req.path.length > 1) {
var query = req.url.slice(req.path.length);
return res.redirect(301, req.path.slice(0, -1) + query);
}
next();
});
// Delete expired Mongo sessions from DB
app.use(function (req, res, next) {
req.session._garbage = new Date();
req.session.touch();
next();
});
/**
* Setting Cache control header for Ajax requests to 30 minutes
*/
app.use(function (req, res, next) {
if(req.xhr){
res.header('Cache-Control', 'max-age=' + 1800 + ', public');
}
next();
});
app.use(compression());
app.use(
multer({
dest: config.get('uploads').folders.temp
})
);
app.use(passport.initialize());
app.use(passport.session());
var initPassport = require('./passport/init');
initPassport(passport);
app.use(function (req, res, next) {
res.locals = {
root : 'http://' + req.headers.host,
sitename : require('./config').get('sitename'),
config: config.get('env'),
url : config.get('url'),
user : req.user,
flash : req.flash()
};
next();
});
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN'){
return next(err);
}
if(req.xhr){
return res.ok({payload: null}, '403 invalid csrf token');
}
// TODO handle CSRF token errors here
res.status(403);
res.send('form tampered with')
});
// This is never called when throwing errors like
// next(new Error('some error') or
// next({status: 500, message:'server error'});
app.use(function (err, req, res, next) {
console.error(err.stack);
// render an error page
});
};

The problem is, that your error handlers should always be at the end of your application stack. This means, that you can either move the error handler from your middleware to your app.js and use them after your requires (app.use()) or include your routes before your middleware.

Note: your error handler middleware MUST have 4 parameters: error, req, res, next. Otherwise your handler won't fire.

I had the same issue, spend the whole day on troubleshooting the issue. Finally, found a simple fix. This worked for me perfectly. You need to place the customer error handler right before the listener handler as below on the server instance file (App.js / server.js).
Good luck :)
app.use((error, req, res, next) => {
if (res.headersSent) {
return next(err)
}
res.status(500).send('INTERNAL SERVER ERROR !')
});
app.listen(3000, function() {
console.log('Node app is running on port 3000');
});
module.exports = app;

For handling errors that are thrown during asynchronous code execution in Express (versions < 5.x), you need to manually catch and invoke the in-built error handler (or your custom one) using the next() function.
app.get('/', (req, res, next) => {
setTimeout(() => {
try {
console.log('Async code execution.')
throw new Error('Unexpected error on async!')
} catch (error) {
// manually catching and propagating to your error handler
next(error)
}
}, 100)
})
// as a last declaration in your entrypoint
app.use((err, req, res, next) => {
// do some error handling here. if you do not call next, you must return a response from here.
})

Your error handler should always be at the end of your application stack.
Apparently it means not only after all app.use() but also after all your app.get() and app.post() calls.

If you don't want to write three parameters for every async router handler to be able to catch errors globally:
npm install express-async-errors
import 'express-async-errors';
app.get('/api/endpoint', async (req, res) => {
const user = await User.findByToken(req.get('authorization'));
if (!user) throw Error("access denied"); //will propagate to global error handler
});

Related

Am I doing anything wrong here node+express + vue SPA getCSRFTOKEN()

My index.js Server
// USE STRICT;
const express = require('express');
const app = express();
const session = require('express-session');
const http = require('http').Server(app);
const socket = require('socket.io');
const schedule = require('node-schedule');
const cors = require('cors');
const io = socket(http, {
cors: {
origin: 'http://localhost:8080',
methods: ['GET', 'POST'],
allowedHeaders: ['my-custom-header'],
credentials: true
}
});
const port = 8080;
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/uploads'));
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const mustacheExpress = require('mustache-express');
app.engine('html', mustacheExpress());
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
const secret = 'somesecretkeyhere';
const passport = require('passport');
const helmet = require('helmet');
const { sendMail } = require('./controllers/sellerAdsController');
// Gives us access to variables set in the .env file via `process.env.VARIABLE_NAME` syntax
// require('dotenv').config();
// Must first load the models before passport
require('./models/user');
// Pass the global passport object into the configuration function
require('./config/passport')(passport);
// This will initialize the passport object on every request
app.use(passport.initialize());
// Allows our remote applications to make HTTP requests to Express application
app.use(cors());
app.use(helmet());
app.use(express.urlencoded({ extended: false }));
// app.use(express.json()); //WARNING: Do not turn on. stops formidable for api calls
app.use(cookieParser(secret));
app.use(session({
secret: secret,
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: true
}
}));
app.use(csrf());
// Stop page caching
app.use(function (req, res, next) {
res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
next();
});
// Imports all of the routes from ./routes/index.js
app.use(require('./routes/api/v1'));
// Socket Operations
// io.on('connection', io => {
// let sessionId = io.id;
// io.on('clientHandshake', (data) => {
// console.log(data);
// io.emit('serverHandshake', { sessionId: sessionId });
// });
// });
// io.use((socket, next) => {
// const username = socket.handshake.auth.username;
// if (!username) {
// return next(new Error('invalid username'));
// }
// console.log(username);
// socket.username = username;
// next();
// });
io.on('connection', (socket) => {
console.log('👾 New socket connected! >>', socket.id);
// notify existing users
socket.broadcast.emit('user connected', {
userID: socket.id,
username: socket.username,
});
socket.on('private message', ({ content, to }) => {
socket.to(to).emit('private message', {
content,
from: socket.id,
});
console.log(content, to);
});
});
// EROOR HANDLING ROUTES MUST BE BENEATH ALL APP.USE AND ROUTES
// Check if request is from web or app (HTML/JSON)
// Handle 404
app.use(function (req, res) {
res.status(404);
res.render('404.html', { title: '404: File Not Found' });
});
// Handle 500
app.use(function (error, req, res) {
return res.send(error);
// res.status(500);
// res.render('500.html', { title: '500: Internal Server Error', error: error });
});
// SCHEDULED JOBS
const now = new Date();
let date = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 0, 0);
schedule.scheduleJob(date, sendMail);
http.listen(port, () => {
console.log(`listening on *:${port}`);
});
And this is how I am getting from VUE
window.axios.get('/databank/getCSRF').then((response) => {
window.axios.defaults.headers.common['XSRF-TOKEN'] = response.data;
}, (err) => {
console.log(err)
})
And this is my login request header
XSRF-TOKEN from my login request header sent by axios
So Ive set my server up like that, and my vue SPA, but getCSRF() seems to be getting the request but I can't do a POST request back to the server throws an error
ForbiddenError: invalid csrf token
at csrf
Maybe because you wrote XSRF-TOKEN instead of CSRF-Token as it suggests in the Express Documentation.

Custom middleware express.js framework ordering

I am writing custom middleware(s) to send request data(like request path, response code, request timestamp, etc) to a remote server on every request. I'm unable to figure out the ordering of my middlewares.
I have created 2 middlewares,
a) process_request (m1) --> this middleware just adds a timestamp to the request object to register when the request was received.
b) process_response (m2) --> this middleware posts the required data to the remote server
m1
function process_request(req, res, next) {
req.requestTime = Date.now()/1000;
next();
}
m2
function process_response(req, res, next) {
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.response_code = res.statusCode;
send_perf_request(request_obj); // sends remote https request not shown here
}
I can think of two ordering options in my app.js:
Order 1:
m1
route 1
route 2
...
route n
m2
404 request handler middleware
Order 2:
m1
route 1
route 2
...
route n
404 request handler middleware
m2
404 request handler middleware
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
The problem with order 1 is that I won't be able to catch 404 requests, which I want to.
The problem with order 2 in all in request the responseCode=404 as the 404 request handler middleware is doing that.
I am new to node.js & confused if I am thinking about this the right way.
My app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const custom_middleware = require("custom_middleware");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(custom_middleware.process_request); //*calling middleware
app.use('/', indexRouter);
app.use('/users', usersRouter);
//catch 404 and forward to error handler
// app.use(function(req, res, next) {
// console.log("in 404 handler");
// //next(createError(404, "this page doesn't exist;"));
// next();
// });
// error handler
app.use(function(err, req, res, next) {
// this is called only in case of errors
// set locals, only providing error in development
console.log("in error handler");
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
console.log(res.statusCode);
//res.render('error');
next(err);
});
app.use(custom_middleware.process_exception); //*calling middleware
module.exports = app;
Custom middleware file
function process_request(req, res, next) {
// this middleware adds request timestamp
console.log("in process request middleware");
req.requestTime = Date.now()/1000;
res.on("finish", function (){
// this middleware collects performance data
console.log("in process response middleware");
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.ip_address = ip.address();
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.requester = null;
request_obj.response_code = res.statusCode;
console.log(request_obj.response_code);
send_perf_request(request_obj);
})
next();
}
function process_exception(err, req, res, next) {
// this middleware collects exception data
console.log("in process exception middleware");
const error_obj = {};
error_obj.path = req.protocol + "://" +
req.hostname + req.originalUrl;
error_obj.exception = "error occured";
error_obj.traceback = err.stack;
error_obj.user = null;
error_obj.ip_address = ip.address();
send_exception_request(error_obj);
next();
}
My routes/index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
throw new Error('this does not exist'); // error manually triggered
res.status(500);
});
module.exports = router;
As mentioned in the comments, abstract the middlewares to avoid defining a set of middlewares on each route.
To replace m1 Create a global middleware, which you define before all your other middlewares which sets res.on("finish", function () {}) event handler to do something when the route is done. I'm pretty sure this is the only event you will get the actual correct res.statusCode if you're doing res.status() anywhere.
Then move your 404 error handler logic into the main error handler, you can then check for status code and all that good stuff before logging and responding. You can also use req.xhr to determine how to respond.
Also, you can use a catch-all to pick up on route 404's: app.get('*', function (req, res, next) { then fire off an error which the error handler handles.
Here is an example putting it all together:
const express = require('express')
const app = express()
// logger middleware
app.use((req, res, next) => {
// set something
req.requestTime = Date.now() / 1000;
// gets fired after all routes have been handled
res.on("finish", function () {
//
req.finishTime = Date.now() / 1000;
// do something with req, res objects
console.log('[in logger] originalUrl:', req.originalUrl)
console.log('[in logger] status code:', res.statusCode)
})
next()
})
// apply routes
// ...
app.get('/', (req, res, next) => {
try {
// example: look for something, which is not found
const mock = false
if (!mock) {
let err = new Error('Mock was not found!')
err.status = 404
throw err
}
res.send('Hello World!')
} catch (err) {
next(err)
}
})
// app.get('/foo', ...
// not found route (catch all)
app.get('*', (req, res, next) => {
let err = new Error('Page not found!')
err.status = 404
next(err)
})
// error handler (will catch everything even uncaught exceptions)
app.use((error, req, res, next) => {
//
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
res.header('Expires', '-1')
res.header('Pragma', 'no-cache')
// set status from error
res.status(error.status || 500)
if (req.xhr) {
// is ajax call, send error as json
res.json({
error: error.name || 'Server error',
status: error.status || 500,
message: error.message || 'Internal server error'
})
} else {
// render a view instead (would need to add a view engine)
res.render('error/' + (error.status || 500), error)
}
})
app.listen(3000, function () {
console.log('App listening on port 3000!')
})

Express router.post not executing block, but found

In my index.js file, each form action called executes properly:
routes/index.js
var express = require('express');
var async = require('async');
var common = require('./common.js');
var log = require('./log.js');
var router = express.Router();
router.post('/premise', function...
router.post('/follow', function...
views/index.jade
extends layout
block js
script(src='/javascripts/countdown.js')
script(src='/javascripts/table_filter.js')
block content
if username...
...div.followers.table_wrapper
h3.table_header.center Following
#followers_tbl.center
h4.center You are not following anyone...
form(action="follow" method="post")
input(type="text" name="follow_username" id="follow_username" placeholder="Add username here...")
button(class="btn btn-primary btn-sml" type="submit" style="margin-bottom: 15px") Follow A User
div#add_premise.textarea_wrapper
form(action="premise" name="premise" method="post")
textarea(name="premise" id="premise" placeholder="Enter your..." required)
p.countdown.black
button(class="btn btn-success" type="submit") Add Premise
But when I call from another page /signup it doesn't work. (See below EDIT file details).
The call returns a 302, so it is found, but the function doesn't execute. But when I remove action='signup/signup/' from the form in the view, then change the router.post('/signup', function... to router.post('/', function..., the function executes, after also returning a 302 in my log.
Can anyone help me? Thanks so much!
EDIT: (Adding app.js and more of routes/signup.js)
app.js
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 session = require('express-session');
var fileStore = require('session-file-store')(session);
var index = require('./routes/index.js');
var signup = require('./routes/signup.js');
var login = require('./routes/login.js');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
var session_options = {
name: 'server-session-cookie-id',
secret: 'secret',
saveUninitialized: true,
resave: true,
store: new fileStore(),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 // 1 year
}
};
// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session( session_options ));
app.use( function( req, res, next ) {
try {
if ( ( req.session && req.session.username ) ||
req.originalUrl == "/login" ||
req.originalUrl == "/signup" {
next();
}
else {
res.redirect('/login');
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
app.use('/', index);
app.use('/signup', signup);
app.use('/login', login);
app.get('/logout', function(req, res, next) {
try {
req.session.destroy();
res.clearCookie(session_options.name);
res.redirect('/');
}
catch( err ) {
common.send_500( req, res, err );
}
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
routes/signup.js
var express = require('express');
var router = express.Router();
var encrypt = require('./encrypt.js');
var common = require('./common.js');
var log = require('./log.js');
/* GET signup page. */
router.get('/', function(req, res, next) {
try {
var signup_error = req.session.signup_error;
req.session.signup_error = null;
res.render('signup', { title: 'Signup', signup_error: signup_error });
}
catch( err ) {
common.send_500( req, res, err );
}
});
router.post('/normal', function(req, res, next) {...});
router.post('/submit', function(req, res, next) {...});
module.exports = router;
views/signup.jade
extends layout
block js
script(src='/javascripts/dob_validate.js')
block content
div#signup_form
form(action="/signup/normal" name="signup" method="post")
input(type="email" name="email" id="email" placeholder="Email" required autofocus)
input(type="text" name="username" id="username" placeholder="Username" required)
input(type="password" name="password1" id="password1" placeholder="Password" required)
input(type="password" name="password2" id="password2" placeholder="Confirm Password" required)
p#dob
| Birthdate
input(type="date" name="birthdate" id="birthdate" style="margin-left=20px;" required).pull-right
label
input(style="margin-right: 5px;" type="checkbox" name="checkbox" required).pull-left
| Check here to indicate that you have read, understand and agree to the
a(href="/terms") Terms of Use,
a(href="/privacy") Privacy Policy
| and
a(href="/cookies") Cookie Policy
| .
div#signup_btns
button(class="btn btn-primary" type="submit") Sign Up
a(href='/login') Login
/signup/normal/ handler:
router.post('/normal', function(req, res, next) {
try {
var email = req.body.email;
var username = req.body.username.toUpperCase();
var password1 = req.body.password1;
var password2 = req.body.password2;
var birthdate = req.body.birthdate;
birthdate = birthdate.substring(5,7) + '/' +
birthdate.substring(8,10) + '/' +
birthdate.substring(0,4);
if (password1 === password2) {
var body = JSON.stringify({
"username" : username,
"password_hash" : encrypt.hash(password1),
"credits" : 100,
"email_address" : email,
"birthdate" : birthdate
});
var path = "/accounts/add/";
common.post( path, body, function( data ) {
try {
if ( data && !data.error ) {
console.log(JSON.parse(data.resp));
res.redirect('/login');
}
else {
req.session.signup_error = data.resp;
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
}
else {
req.session.signup_error = "Passwords do not match";
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
This is normal because your are using signup.js router with /signup end point:
const signup = require('./routes/signup.js')
...
app.use('/signup', signup)
Assuming that you define a route handler inside signup.js to /about route:
router.post('/about', function(req, res, next) { ... });
Then the app will be able to handle requests to /signup/about. If you create a route handler for /signup then the end point is /signup/signup
I suggest you to change the /signup route to /user like this: app.use('/user', signup) and also change the form action to action="/user/signup" this makes more sense
You have a mixup here between GET requests and POST requests, and which is on what endpoint.
If you're hitting localhost:3000/signup, then that endpoint should be returning your signup.jade file (by the way this is a server-side html rendering you're using....). You're serving dynamically generated HTML content at that endpoint.
However, By typing in localhost:3000/signup within your browser address bar, you're executing an HTTP GET request, not a POST. Very important difference here!
Thus, you now need a router endpoint of router.post('/signup/signup', function...) to actually catch the form submit (note this is now a POST) from your jade template. Honestly I would want to point out your naming scheme here, because as you can see /signup/signup is quite confusing and doesn't follow a very good convention.

Route callbacks not executing but middleware is

So this project was working fine before today, but after a bit of a nooby mistake with git I broke my project, and was unable to recover the commit. After spending some time getting everything fixed, my routes are now broken. My issue is that when calling my API routes now the server hangs for exactly 1 minute, then times out and logs a 404 on the server.
To give some background I'm using this boilerplate. In my debugging process I basically put console.logs everywhere I possibly could, and it looks like all my initialization middleware for express and passport are working fine, and my code is getting to where the routes are defined with no errors.
Middleware with app.use works and all checks out when a request is made, and all console.logs I've put there show fine. The console.logs only stop appearing in the final route definition like in this example:
app.get('/route', function(req, res, next) {
console.log('this never shows');
next();
}, function(req, res, next) {
console.log('this never shows');
})
My actual routes do have a res.send(), this is just an example to show you. All the console.logs I put in the middleware before this route show when the request is made, so it is hanging somewhere in app here.
It's a rather large project, so if you want specific code examples just ask and I'll post it. But I was able to recover all the important files that I had saved somewhere else and I'm pretty sure all my code is back to how it was before now.
edit:
Here is my file with express middleware definitions:
config/express.js
/**
* Module dependencies.
*/
var express = require('express');
var MongoStore = require('connect-mongo')(express);
var flash = require('connect-flash');
var helpers = require('view-helpers');
var swig = require('swig');
var session = require('express-session');
module.exports = function (app, config, passport) {
app.set('showStackError', true);
// should be placed before express.static
app.use(express.compress({
filter: function (req, res) {
return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
},
level: 9
}));
app.use(express.favicon());
app.use(express.static(config.root + '/public'));
app.use('/uploads', express.static(config.root + '/uploads'));
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.status(204).end();
}
else {
next();
}
};
app.use(allowCrossDomain);
// don't use logger for test env
if (process.env.NODE_ENV !== 'test') {
app.use(express.logger('dev'));
}
// set views path, template engine and default layout
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', config.root + '/app/views');
app.set('view cache', process.env.NODE_ENV !== 'development');
app.configure(function () {
// dynamic helpers
// app.use(function(req,res,next){
// req.locals.session = "eeeeeeee";
// next();
// });
// cookieParser should be above session
app.use(express.cookieParser());
// bodyParser should be above methodOverride
app.use(express.bodyParser());
app.use(express.methodOverride());
// express/mongo session storage
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
session({
secret: 'secrettexthere',
saveUninitialized: true,
resave: true,
store: new MongoStore({
url: 'mongodb://localhost/traderdb',
db: 'traderdb',
collection: 'sessions',
auto_reconnect: true
})
})(req, res, next);
}
});
// connect flash for flash messages
app.use(flash());
app.use(function (req, res, next) {
res.locals.session = req.session;
res.locals.req = req;
next();
});
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.initialize()(req, res, next);
}
});
//app.use(helpers('app name'));
//
// use passport session
app.use(function(req, res, next) {
if(!req.cookies['_ga']) {
next();
}
else {
passport.session()(req, res, next);
}
});
// routes should be at the last
app.use(app.router);
// assume "not found" in the error msgs
// is a 404. this is somewhat silly, but
// valid, you can do whatever you like, set
// properties, use instanceof etc.
app.use(function(err, req, res, next) {
// treat as 404
if (~err.message.indexOf('not found')) return next();
// log it
console.error(err.stack);
// error page
res.status(500).render('500', { error: err.stack });
});
// assume 404 since no middleware responded
app.use(function(req, res, next) {
res.status(404).render('404', { url: req.originalUrl, error: 'Not found' })
});
})
}
I also have another file with passport route definitions if you'd like to see that too, but all that is tested and works okay too.
edit 2:
This is my entry point file:
server.js
/**
* Module dependencies.
*/
var express = require('express')
, fs = require('fs')
, passport = require('passport');
/**
* Main application entry file.
* Please note that the order of loading is important.
*/
// Load configurations
// if test env, load example file
var env = process.env.NODE_ENV || 'development'
, config = require('./config/config')[env]
, auth = require('./config/middlewares/authorization')
, mongoose = require('mongoose');
// Bootstrap db connection
mongoose.connect(config.db);
// Bootstrap models
var models_path = __dirname + '/app/models'
fs.readdirSync(models_path).forEach(function (file) {
require(models_path+'/'+file);
});
// bootstrap passport config
require('./config/passport')(passport, config);
var app = express();
// express settings
require('./config/express')(app, config, passport);
// Bootstrap routes
require('./config/routes')(app, passport, auth);
// Start the app by listening on <port>
var port = 3002;
app.listen(port);
console.log('Express app started on port '+port);
// expose app
exports = module.exports = app;
edit 3:
Here are my route definitions:
config/routes.js
var express = require('express');
var path = require('path');
var fileManager = require('express-file-manager');
var mongoose = require('mongoose');
var Session = mongoose.model('Session');
module.exports = function (app, passport, auth) {
var users = require('../app/controllers/users');
var coupons = require('../app/controllers/coupons');
var magazines = require('../app/controllers/magazines');
var zones = require('../app/controllers/zones');
var transactions = require('../app/controllers/transactions');
var favorites = require('../app/controllers/favorites');
var banners = require('../app/controllers/banners');
var reports = require('../app/controllers/reports');
var coverContest = require('../app/controllers/coverContest');
var contactMessage = require('../app/controllers/contactMessage');
app.post('/api/users/login', users.login);
app.post('/api/users/register', users.register);
app.post('/api/users/logout', users.logout);
app.post('/api/users/sendResetEmail', users.sendResetEmail);
app.post('/api/users/changePassword', users.changePassword);
app.post('/api/users/redeemCoupon', isValidAppUser(), users.redeemCoupon);
app.get('/api/users/validate', isLoggedIn(0), function(req, res) {
res.send(req.user);
});
app.post('/api/coupons', coupons.get);
app.post('/api/coupons/import', isLoggedIn(0), coupons.import);
app.post('/api/coupons/remove', isLoggedIn(0), coupons.remove);
app.post('/api/coupons/upload', isLoggedIn(0), coupons.upload);
app.post('/api/transactions', transactions.get);
app.post('/api/allTransactions', isLoggedIn(0), transactions.getAll);
app.post('/api/magazines', magazines.get);
app.post('/api/magazines/import', isLoggedIn(0), magazines.import);
app.post('/api/magazines/remove', isLoggedIn(0), magazines.remove);
app.post('/api/banners', banners.get);
app.post('/api/banners/import', isLoggedIn(0), banners.import);
app.post('/api/banners/remove', isLoggedIn(0), banners.remove);
app.post('/api/favorites', isValidAppUser(), favorites.get);
app.post('/api/favorites/import', isValidAppUser(), favorites.import);
app.post('/api/zones', zones.get);
app.post('/api/zones/add', zones.add);
app.post('/api/zones/addCoupon', zones.addCoupon);
app.post('/api/zones/addMagazine', zones.addMagazine);
app.post('/api/mail/ccSubmit', coverContest.ccSubmit);
app.post('/api/mail/contactSubmit', contactMessage.contactSubmit);
//app.get('/api/reports/siteUsers', reports.siteUsers);
app.get('/auth/facebook', passport.authenticate('facebook', { scope: [ 'email', 'user_about_me'], failureRedirect: '/login' }), users.signin);
app.get('/auth/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/github', passport.authenticate('github', { failureRedirect: '/login' }), users.signin);
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/twitter', passport.authenticate('twitter', { failureRedirect: '/login' }), users.signin);
app.get('/auth/twitter/callback', passport.authenticate('twitter', { failureRedirect: '/login' }), users.authCallback);
app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/', successRedirect: '/main.html' }));
}
function isLoggedIn(secLvl) {
return function(req, res, next) {
if(req.isAuthenticated() && req.user.secLvl <= secLvl && req.user.google.email.includes('#bizpub36.com')) {
return next();
}
res.redirect('https://accounts.google.com/logout');
}
}
function isValidAppUser() {
return function(req, res, next) {
Session.findOne({ sess_id: req.body.sess_id }).exec(function(err, session) {
if(!err && session) {
next();
}
else {
res.end({ status: 'error', message: 'invalid session' });
}
});
}
}
If app.use works, my guess would be your protocol, is the app.get correct? You issue is otherwise located somewhere else in your code base as your sample runs fine as a single route express app.
It sounds like one of three things:
Somewhere in your middleware chain, you are not calling next() to allow it to advance to the next level of handlers and thus the request just eventually times out waiting for that middleware to finish (this seems to match the symptoms you describe).
Somehow, your app.get() doesn't actually match the route you expect it to or is not specified correctly.
You're using a router, but have not configured it correctly.
But, because you don't get an immediate 404, but rather it times out, it is probably option #1 above.

express session management not working

I am new to the node.js world. I am trying to write a REST services and I am stuck with session management. So I created a separate app just to see if I can get the session to work, but it doesn't, here is the code. The req.session.username is always undefined:
var express = require('express');
var url = require('url');
var app = express()
app.use(express.cookieParser('Hiren'))
app.use(express.session({ secret: 'HirenAdesara' }))
app.use(express.bodyParser())
app.use(app.router)
//Sniff HTTP
app.all('*', function(req, res, next) {
//Check for Authentication
console.log(req.session)
if ((!(/^\/auth/g.test(req.url))) && (!req.session)) {
console.log('in app.all: Unauthorized')
res.send(401)
}
else
{
return next()
}
})
app.post('/auth', function(req, res) {
var query = req.body
console.log('Query' + JSON.stringify(query))
username = query.username;
password = query.password;
if(username == 'Hiren' && password == 'Adesara')
{
req.session.username = 'Hiren';
console.log('New Session Created..')
res.send(200)
}
else
{
console.log('New session could not be created.')
res.send(401)
}
})
app.get('/projects', function(req,res) {
console.log('inside projects' + req.session.username);
res.send(req.session.username); })
app.listen(2048)
console.log('Listening on port 2048...')
It doesn't work and I have no idea what is wrong here.
Star by moving the 3 lines in your app.get('/'...) outside of it:
var express = require('express');
var querystring = require('querystring');
var app = express()
app.use(express.cookieParser('Hiren')); // This line
app.use(express.session({ secret: 'HirenAdesara' })); // This line
app.use(express.bodyParser()); // This line
app.get('/', function(req, res){
res.send('hello from the root page');
})
// the rest of your code

Resources