SendFile issues regarding control flow in Express Middleware

Here is an important part of my code simplified to narrow down the problem:

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"); } 

The question is: in the case of an html extension, for example, I would not expect middleware2 to be called at all, but it will do it! It seems that sendFile initiates sending the file, and the control fails before the sendFile callback is called. If I replaced break1 with next () or return next (), which would be equally wrong - Control will move on to the next middleware2 before the sendFile callback is executed. How do I get middleware2 to receive calls for the first set of extensions? In addition, if the extension "njm", even without the following (), is called middleware2. Why?

Please do not suggest using static Express middleware, because I have some logic involved in serving various types of files, which are more complex than the simplified scenario above.

0
express
source share
2 answers

res.sendFile() bit unique. If you don't pass it a completion callback, then it will call next() for you. See later in this answer for more details.


What you are reporting is the opposite of how Express says it works, so I think there should be something that is not quite the way you are reporting it.

The whole point of Express middleware is that any middleware call gets the opportunity to fill out a request, and then it either processes the request, generating a response, or if it wants the middleware chain to continue, then it calls next() , If next() not called, the middleware chain stops and nothing is called in the current middleware chain. If this is application-level middleware (with app.use() ), then there should be no additional middleware processing at the application level unless you call next() from your middleware.

Here's a quote from the Middleware Express Page :

If the current middleware does not complete the request-response cycle, it must call next () to transfer control to the next middleware, otherwise the request will be left hanging.

This is a pretty good article about Express: Express Middleware Demystified middleware that helps explain a lot more details. It also confirms that if you do not call next() , then no more handlers will be called in the middleware chain.


There is one special case with res.sendFile() . If you do not pass it a completion callback, it will call next() . If you pass it a completion callback, it will not call next() . This does not look well documented, but if you look at the res.sendFile() code here , you can see how it works.


One thing to consider when debugging is that sometimes the browser issues more requests than you can understand. For example, when you first got to the website’s homepage, the browser may ask for favicon, which causes an additional request to your web server. So, I am wondering if your debugging console.log() will confuse you, because maybe it includes more than one request, and not one request that goes through both middleware components. In addition, a cross-origin Ajax call may request AJAX parameters before requesting an actual Ajax call.

You can distinguish several requests like this, and more accurately see if it really goes from middleware1 to middleware2 for the same request:

 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); } 

Also, it seems that the middleware1 tags in which you are not processing the request should call next() , so for this I changed the middleware1 above. If you process the request in a switch statement, then return . If not, it will go to the next() call.

+2
source share

Once you write app.use(middleware2) , middleware2 will be used for all routes on the app after middleware1 is fully launched.

As you want to use middleware2 conditionally, I suggest you use the following method:

 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"); } 
+1
source share

All Articles