简体   繁体   中英

Node.js with exress handle database connection error outside middleware

I am fairly new to node and I am trying to create a simple login/signup system with passportjs. I have my passport configuration file in which i pass the passport object as a parameter as you can see below.

My passport configuration file:

var LocalStrategy   = require('passport-local').Strategy;

var User = require('./../models/user');

var mysql = require('./../database/mysql_setup');
var mysqlPool = mysql.pool;

// expose this function to our app using module.exports
module.exports = function(passport) {

    mysqlPool.getConnection(function(error, connection) {

        if (error) throw error;

        connection.query('USE vidyawxx_build2');

        // =========================================================================
        // passport session setup ==================================================
        // =========================================================================
        // required for persistent login sessions
        // passport needs ability to serialize and deserialize users out of session

        // used to serialize the user for the session
        passport.serializeUser(function(user, done) {
            done(null, user.username);
        });

        // used to deserialize the user
        passport.deserializeUser(function(username, done) {
            connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE username = " + connection.escape(username), function(err,rows){
                done(err, rows[0]);
            });
        });


        // =========================================================================
        // LOCAL SIGNUP ============================================================
        // =========================================================================
        // we are using named strategies since we have one for login and one for signup
        // by default, if there was no name, it would just be called 'local'

        passport.use('local-signup', new LocalStrategy({
                // by default, local strategy uses username and password, we will override with email
                usernameField : 'username',
                passwordField : 'password',
                passReqToCallback : true // allows us to pass back the entire request to the callback
            },
            function(req, username, password, done) {

                // find a user whose username is the same as the forms username
                // we are checking to see if the user trying to login already exists
                connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE `username` = "+connection.escape(username),function(err,rows){
                    if (err)
                        return done(err);
                    if (rows.length) {
                        return done(null, false, req.flash('error', 'This username is already in use.'));
                    } else {

                        // if there is no user with that username
                        // create the user
                        var newUserMysql = new User(username, password);

                        newUserMysql.generateHash(function(error, hash) {

                            if(error) {
                                return done(error);
                            }

                            var insertQuery = "INSERT INTO `"+mysql.dbSpecs.prefix+"users` ( username, password ) values (" + connection.escape(newUserMysql.username) +",'"+ hash +"')";

                            connection.query(insertQuery,function(err,rows){
                                if(err) {
                                    return done(error);
                                }
                                return done(null, rows);
                            });

                        });

                    }
                });
            }
        ));

        // =========================================================================
        // LOCAL LOGIN =============================================================
        // =========================================================================
        // we are using named strategies since we have one for login and one for signup
        // by default, if there was no name, it would just be called 'local'

        passport.use('local-login', new LocalStrategy({
                // by default, local strategy uses username and password
                usernameField : 'username',
                passwordField : 'password',
                passReqToCallback : true // allows us to pass back the entire request to the callback
            },
            function(req, username, password, done) { // callback with email and password from our form

                connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE `username` = " + connection.escape(username), function(err,rows){

                    if (err) {
                        return done(err);
                    }
                    if (rows.length === 0) {
                        return done(null, false, req.flash('error', 'Oops! Wrong username or password.')); // req.flash is the way to set flashdata using connect-flash
                    }

                    // if the user is found but the password is wrong
                    var newUser = new User(username, password);
                    newUser.compareHash(function(error, result) {
                        if(result) {
                            return done(null, rows[0]);
                        } else {
                            return done(null, false, req.flash('error', 'Oops! Wrong username or password.')); // create the loginMessage and save it to session as flashdata
                        }
                    });

                });



            }
        ));

        connection.release();

    });
};

My problem lies in the fact that if my mysql server is down for any reason, the error is thrown in my first line. I want to be able to redirect my users to a simple page that gives him a message like "Something is wrong with the database, please try later". The thing is, when i throw the error, my app just shuts down giving any visitor the ERR_CONNECTION_REFUSED response.( I am currently working this locally.

This is my app.js file:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var passport = require('passport');
var passportConfig = require('./config/passport');

var session = require("express-session");
var flash = require("connect-flash");

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();
app.use(express.static(path.join(__dirname, 'public')));

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

passportConfig(passport);
app.use(session({
    secret: "aPa1fgOed(&fjkKLN34%#$lpv@@",
    resave: true,
    saveUninitialized: true,
    cookie: { maxAge: 1000*60*15 } //15 minutes in milliseconds
}));

app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

//create local vaariables for all our templates to use
app.use(function(req, res, next) {
    res.locals.errors = req.flash("error");
    res.locals.infos = req.flash("info");
    res.locals.successes = req.flash("success");
    next();
});

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

That being said, since the error is thrown in my passport-config file, which doesnt follow the middleware convention of having a req, res and next params, how can i redirect my users to a page like the one mentioned above gracefully ?

Just to be sure, I will say this again, this concerns only mysql connection errors. I know that i can return other errors through my passport-config methods by using done(), but database connection errors occur outside the functions with a done param.

Thanks in advance

Looks like after some digging around the only workaround I could think of is incorporating the queries inside the passport configuration methods so that I could pass back any database connection error through the done() function;

This is my modified passport config file

// load all the things we need
var LocalStrategy   = require('passport-local').Strategy;

var User = require('./../models/user');

var mysql = require('./../database/mysql_setup');
var mysqlPool = mysql.pool;

// expose this function to our app using module.exports
module.exports = function(passport) {

    //connection.query('USE vidyawxx_build2');

    // =========================================================================
    // passport session setup ==================================================
    // =========================================================================
    // required for persistent login sessions
    // passport needs ability to serialize and deserialize users out of session

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
        done(null, user.username);
    });

    // used to deserialize the user
    passport.deserializeUser(function(username, done) {
        mysqlPool.getConnection(function(dbError, connection) {

            if(dbError) {
                return done(dbError);
            }

            connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE username = " + connection.escape(username), function(err,rows){
                if(err) {
                    done(err);
                    connection.release();
                    return;
                }
                connection.release();
                done(err, rows[0]);
            });
        });
    });


    // =========================================================================
    // LOCAL SIGNUP ============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-signup', new LocalStrategy({
            // by default, local strategy uses username and password, we will override with email
            usernameField : 'username',
            passwordField : 'password',
            passReqToCallback : true // allows us to pass back the entire request to the callback
        },
        function(req, username, password, done) {

            // find a user whose username is the same as the forms username
            // we are checking to see if the user trying to login already exists
            mysqlPool.getConnection(function(dbError, connection) {

                if(dbError) {
                    return done(dbError);
                }

                connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE `username` = "+connection.escape(username),function(err,rows){

                    if (err) {
                        connection.release();
                        return done(err);
                    }

                    if (rows.length) {
                        connection.release();
                        return done(null, false, req.flash('error', 'This username is already in use.'));
                    } else {

                        // if there is no user with that username
                        // create the user
                        var newUserMysql = new User(username, password);

                        newUserMysql.generateHash(function(error, hash) {

                            if(error) {
                                connection.release();
                                return done(error);
                            }

                            var insertQuery = "INSERT INTO `"+mysql.dbSpecs.prefix+"users` ( username, password ) values (" + connection.escape(newUserMysql.username) +",'"+ hash +"')";

                            mysqlPool.query(insertQuery,function(err,rows){
                                if(err) {
                                    connection.release();
                                    return done(error);
                                }
                                connection.release();
                                return done(null, rows);
                            });

                        });

                    }
                    connection.release();
                });

            });
        }
    ));

    // =========================================================================
    // LOCAL LOGIN =============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-login', new LocalStrategy({
            // by default, local strategy uses username and password
            usernameField : 'username',
            passwordField : 'password',
            passReqToCallback : true // allows us to pass back the entire request to the callback
        },
        function(req, username, password, done) { // callback with email and password from our form

            mysqlPool.getConnection(function(dbError, connection) {
                if(dbError) {
                    return done(dbError);
                }
                connection.query("SELECT * FROM `"+mysql.dbSpecs.prefix+"users` WHERE `username` = " + connection.escape(username), function(err,rows){

                    if (err) {
                        connection.release();
                        return done(err);
                    }
                    if (rows.length === 0) {
                        connection.release();
                        return done(null, false, req.flash('error', 'Oops! Wrong username or password.')); // req.flash is the way to set flashdata using connect-flash
                    }

                    // if the user is found but the password is wrong
                    var newUser = new User(username, password);
                    newUser.compareHash(function(error, result) {
                        if(result) {
                            connection.release();
                            return done(null, rows[0]);
                        } else {
                            connection.release();
                            return done(null, false, req.flash('error', 'Oops! Wrong username or password.')); // create the loginMessage and save it to session as flashdata
                        }
                    });

                });
            });
        }
    ));


};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM