关于Express Middleware中控制流的SendFile问题

时间:2015-11-17 03:24:48

标签: node.js express

以下是我的代码的相关部分,为简化问题而简化:

 app.use(middleware1);
 app.use(middleware2);

function middleware1(req,res,next) {
  ...//get extension of request URL
  switch (extension)
  {
    case 'js'  :
    ..
    case 'html': res.sendFile(res.originalUrl,function(err) {});
                 break; //break1
    case 'njm' : break; //break2
    default    : console.log('default');
                 break;
  }
}

function middleware2(req,res,next) {
   console.log("I am in middleware2");
}

问题是这样的:例如,如果扩展名是html,我不会期望中间件2被调用,但确实如此! 似乎sendFile启动文件的发送,控制执行在调用sendFile的回调之前就已经过了。如果我将next1替换为next()或者返回next()同样存在缺陷 - 在执行sendFile的回调之前,Control将转到下一个middleware2。如何阻止中间件2被调用第一组扩展?此外,如果扩展名为“njm'”,即使没有next(),也会调用middleware2。为什么呢?

请不要建议使用Express静态中间件,因为我有一些逻辑涉及提供不同的文件类型,这比上面给出的简化方案更复杂。

2 个答案:

答案 0 :(得分:2)

res.sendFile()有点独特。如果您未通过完成回调,则会为您拨打next()。请参阅本答复后面的详细信息。

您所报告的内容与Express表示它的工作方式相反,所以我认为必须有一些事情不会像您报告的那样发生。

Express中间件的重点在于,任何给定的中间件调用都有机会对请求进行字段处理,然后通过生成响应来处理请求,或者如果它希望中间件链继续,则调用{{1 }}。如果未调用next(),则中间件链将停止,并且在当前中间件链中不会调用任何其他内容。如果这是应用程序级中间件(使用next()),那么如果您不从中间件调用app.use(),则不应再进行应用程序级中间件处理。

以下是Express middleware page

的引用
  

如果当前的中间件没有结束请求 - 响应周期,那么   必须调用next()将控制权传递给下一个中间件,否则就是   请求将被搁置。

这篇关于Express中间件的文章非常好:Express Middleware Demystified有助于解释更多细节。它还确认,如果您不打电话给next(),那么将不再在中间件链中调用处理程序。

next()有一个特例。如果您未通过完成回调,则会自行调用res.sendFile()。如果您将完成回调传递给它,那么它将不会调用next()。这似乎没有详细记录,但如果您查看next()代码here,就可以看到它是如何工作的。

有一点需要注意调试,有时候浏览器会发出比你想象的更多的请求。例如,当您第一次访问某个站点的主页时,浏览器可能会要求提供网站图标,这会导致额外的请求到达您的Web服务器。所以,我想知道你的res.sendFile()调试是否让你感到困惑,因为可能有多个请求进入,而不是一个请求通过两个中间件。此外,交叉源Ajax调用也可以在请求实际的Ajax调用之前请求AJAX选项。

您可以区分这样的多个请求,并在同一请求中更准确地查看它是否实际上从console.log()转到middleware1

middleware2

此外,似乎var reqCntr = 1; app.use(middleware1); app.use(middleware2); function middleware1(req,res,next) { if (!req.reqCntr) { req.reqCntr = reqCntr++; } console.log("middleware1: " + req.reqCntr); ...//get extension of request URL switch (extension) { case 'js' : .. case 'html': res.sendFile(res.originalUrl,function(err) {}); // return here because the request is now handled return; case 'njm' : break; //break2 default : console.log('default'); break; } // the request was not handled so call the next link in the middleware chain next(); } function middleware2(req,res,next) { if (!req.reqCntr) { req.reqCntr = reqCntr++; } console.log("middleware2: " + req.reqCntr); } 您未处理请求的情况应致电middleware1,因此我已修改上述next()来执行此操作。如果您在switch语句中处理请求,则middleware1。如果没有,则会调用return

答案 1 :(得分:1)

一旦您编写app.use(middleware2)middleware2将在中间件1完全执行后用于app上的所有路由。

由于您想有条件地使用middleware2,我建议您使用以下方法:

app.use(middleware1);

function middleware1(req,res,next) {
  ...//get extension of request URL
  switch (extension)
   {
    case 'js'  : middleware2(req, res, next);
                 break;

    case 'html': res.sendFile(res.originalUrl,function(err) {});
                 break;

    case 'njm' : middleware2(req, res, next);
                 break;

    default    : middleware2(req, res, next);
                 break;
   }
 }

  function middleware2(req,res,next) {
    console.log("I am in middleware2");
  }