使回调阻塞线程

时间:2016-08-05 05:36:52

标签: node.js express callback

我正在使用ExpressJS中间件来检查数据库中的用户ip并停止响应用户,如果最后一小时有超过7次登录失败,我在数据库连接之前使用检查'/',如果一切正常,则不会发送垃圾邮件数据库。但事实证明,当中间件访问数据库并在回调中进行检查时,第一个else中的代码会运行。这是我的中间件:

// check for failed logins from this ip in db
// if allowed number exceeded - stop responding
app.use(function (req, res, next) {
if(req._parsedUrl.pathname === '/') {
    MongoClient.connect(databaseUri || 'mongodb://localhost:27017/dev', function (err, db) {
        assert.equal(err, null);
        var failedLogins = db.collection('FailedLogins');
        failedLogins.find({ip: req._remoteAddress}).toArray(function (err, results) {
            assert.equal(err, null);
            console.log('db check');
            // if ip is in FailedLogins collection
            if (results.length) {
                // if there are more than 7 logins and they haven't expired
                if (results[0].failedLoginsNum >= 7 && parseInt(results[0].expiration) >= parseInt(Date.now())) {
                    res.end();
                } else {
                    next();
                }
            } else {
                next();
            }
        });
    });
} else {
    console.log('next');
    next();
}
});

这是控制台输出:

db check
GET / 200 20.117 ms - -
next
GET /favicon.ico 200 207.559 ms - 1353

1 个答案:

答案 0 :(得分:1)

这是预期的行为,因为建立了早于db连接的第二个请求,并且查找已完成查询。

首先,您不希望在每个请求上创建新连接,这是一种不好的做法。

考虑下一个代码:

dbProvider.js

'use strict';

const MongoClient = require('mongodb').MongoClient;
const CONNECTION_PATH = 'mongodb://localhost:27017/dev';

module.exports.init = cb => {
  MongoClient.connect(CONNECTION_PATH, (err, db) => {
    module.exports.db = db;
    cb(err, db);
  });
};

index.js

'use strict';

const express = require('express');
const app = express();

const dbProvider = require('./dbProvider');
const PORT = 8888;

dbProvider.init((err, db) => {
  if(err) {
    console.error(err);
    process.exit(1);
  }

  app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
});

在index.js中,我们等到建立连接,然后才监听HTTP请求。 下一步是登录,所以如果你想限制登录尝试我建议使用简单的中间件。

index.js已修改

'use strict';

const express = require('express');
const app = express();

const dbProvider = require('./dbProvider');
const PORT = 8888;

dbProvider.init((err, db) => {
  if(err) {
    console.error(err);
    process.exit(1);
  }

  app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
});

// middlewares

function checkAuth(req, res, next) {
  // here check if userId in cookie and match to db record or check token
  if (req.authenticated()) {
    return next();
  } else {
    return res.send(401); // Respond "Unauthorized"
  }
}

function checkLoginAttempts(req, res, next) {
  let failedLogins = dbProvider.db.collection('FailedLogins');
  failedLogins.find({ip: req._remoteAddress}).toArray(function (err, results) {
    if(err) {
      console.error(err);
      return res.status(500).end();
    }
    console.log('db check');

    if(!results.length) return next();

    // if ip is in FailedLogins collection
    // if there are more than 7 logins and they haven't expired
    if (results[0].failedLoginsNum >= 7 && parseInt(results[0].expiration) >= parseInt(Date.now())) {
      res.status(401).send('The maximum number of login attempts has been reached. Please try again in 1 hour.');
    } else {
      next();
    }
  });
}

// routes

app.use('/yourLoginEndPoint', checkLoginAttempts, (req, res) => {
  // login process, set userId as cookie value or create token
});

app.use('/anyOtherEndPoint', checkAuth, (req, res) => {
  // here you sure that user is authenticated
});

如果有任何问题让我知道