【问题标题】:Why is my middleware executed twice?为什么我的中间件执行了两次?
【发布时间】:2016-01-18 19:58:07
【问题描述】:

我在 express.js/node.js 中编写了一个中间件,用于检查会话,如果找到用户 ID,则显示用户的菜单,否则显示默认菜单。

每个页面请求都会检查 id 并从数据库中获取用户数据(id、名称、类别等)

这是中间件:

module.exports = function(req,res,next){
    console.log("INSIDE SESSION HANDLER");

    if(!req.session.uid) return next();
    else{   
        User.get(uid, function(user){
            if (!user) {return next(err);}
            else{
                req.user = res.locals.user = user;
                next();
            }
        })
    }
}

然后,ejs 会检查 locals,如果有 ID,则显示用户的菜单。

我正在加载一个没有使用 socket.io 库的测试页面,但忘记删除 <script src="/socket.io/socket.io.js"></script> 行。

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %> , <%= settings.title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <script src="/socket.io/socket.io.js"></script>//<== SHOULD DELETE THIS
    <h1>Login</h1>
        <%include menu%> //<== USES EJS TO CHECK LOCALS AND SHOW DEFAULT OR USER MENU
  </head>
  <body>

script src 行不存在时,中间件执行一次(只看到一次console.log("INSIDE SESSION HANDLER");)。

script src 行出现时,显然给出了 404 错误,但中间件执行了两次(两次看到了console.log("INSIDE SESSION HANDLER");)。

这是console.log的输出:

INSIDE SESSION HANDLER
GET / 304 66ms //page
GET /stylesheets/style.css 304 11ms
INSIDE SESSION HANDLER //again
GET /socket.io/socket.io.js 404 28ms - 1.04kb
GET /multimedia/01.jpg 304 4ms //images...
GET /multimedia/02.jpg 304 5ms

我想真正了解请求/响应和中间件。那么为什么会这样呢?为什么这个中间件会因为 404 错误而执行两次? 404错误响应是否会导致中间件执行两次?

谢谢

编辑

我像这样在 app.js 中使用中间件:

app.use(favicon(__dirname + '/node_modules/static-favicon/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({resave:'false', saveUninitialized:'false', secret:'secret'}));

app.use(express.static(path.join(__dirname, 'public')));
app.set('multimedia', __dirname + '/public/multimedia');

app.use(handler); //<= THE MIDDLEWARE IN QUESTION


app.use(messages);
app.get('/', routes.list);
app.get('/register', register.form);
app.post('/register', register.submitit);

【问题讨论】:

  • 可能是express.static 无法提供所请求的文件,因此它继续请求链(又名'next()'),在这种情况下它将运行app.use(handler)中间件并尝试与较低的其他路由匹配,不适用,因此它显示 404。换句话说,它为实际页面加载记录一次,为 404 记录一次。
  • 在测试了这种情况后,我已将上述评论写在下面的答案中。

标签: javascript node.js express middleware


【解决方案1】:

您的中间件运行两次的原因是它为初始请求运行一次,为 404(不存在的静态脚本)运行一次。

如果express.static 可以找到一个文件,它会发送带有相应文件的响应,如果它找不到正确的文件(就像你的情况一样)它会调用next(),它会尝试将请求匹配到其他路由,运行在express.static 下定义的任何其他中间件。

换句话说,如果express.static 确实找到了要服务的文件,它下面的中间件将不会运行。在您的情况下,中间件会针对实际请求运行一次,并尝试查找文件或正确的路由以作为响应。

这可以通过将req.path 记录为普通静态文件和不存在的静态文件来观察:

app.js -

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

app.use(function(req, res, next){
  console.log('Requested path: %s', req.path);
  next();
})

layout.hbs(为“/”渲染)-

<!DOCTYPE html>
<html>
  <head>
    <title>{{title}}</title>
    <link rel='stylesheet' href='/stylesheets/style.css' /> <!-- exists -->
    <link rel='stylesheet' href='/stylesheets/foo.css' /> <!-- doesn't exist -->
  </head>
  <body>
    {{{body}}}
  </body>
</html>

我们会看到中间件只会记录初始请求和 404 的请求路径,而不是静态文件。例如:

Requested path: /
Requested path: /stylesheets/foo.css

希望这会有所帮助。

【讨论】:

  • 非常感谢您的回答。我猜在第三段第一行,does fine the file 你的意思是does find the file
【解决方案2】:

你在哪里应用这个中间件?

如果你有

app.use(handler)

如果handler 的格式为function (req, res, next),它将应用于每个 请求(带有任何HTTP 动词)。或者,

app.get('/some/route', handler)

handler 将适用于/some/route 下方的所有位置(例如/some/route/any/deepness)。

所以我的猜测是您过度应用中间件功能(我正在交换 handlermiddleware,它们具有相同的功能签名 - 也许 handler 更适合向那些调用 next() 的人发送响应和中间件的函数)。但是如果没有这个中间件是如何应用的上下文,就很难说。

请参阅Writing Middleware 和 Express 提供的其他指南。

PS。你不需要在中间件中return next()。只需next()

【讨论】:

  • 我更新了我的问题,展示了如何在 app.js 中定义中间件。很好的答案,我明白你在说什么,但我不认为我过度应用它,因为我定义了它,然后它后面的所有其他定义都使用它。这与 404 错误以及中间件执行两次的事实有什么关系?谢谢
  • @slevin 当您请求不存在的东西时,中间件会运行(因为它运行在所有非静态的东西上),然后是打印 404 页面的 404 处理程序。
猜你喜欢
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 1970-01-01
相关资源
最近更新 更多