12

I have a chain of "route specific middleware" for this route, like so:

    var express = require('express');
    var server = express();
    var mw1 = function(req, resp, next) {
        //do stuff
        if (success) {
            next();
        } else {
            req.connection.destroy(); //without calling next()
        }
    };
    var mw2 = function(req, resp, next) {
        //do stuff
        if (success) {
            next();
        } else {
            req.connection.destroy(); //without calling next()
        }
    };
    server.post('/some/path', [mw1, mw2], function(req, resp) {
        //write response
    });

[mw1, mw2] are the middleware specific to the route /some/path.

This is different from server-wide middleware like this:

    server.use(mw1);
    server.use(mw2);

Where it applies to all routes defined.

Now my issue is that I want to exit from the chain. I.e. if success is false in mw1, I do not wish for mw2 to be called. If success is false in mw2, I do not without for the route function to be called. Presently, both mw1 and mw2 appear to be getting called whether or not next() is called - and I do not know why.

How can I go about doing this?

aynber
  • 22,380
  • 8
  • 50
  • 63
bguiz
  • 27,371
  • 47
  • 154
  • 243

3 Answers3

20

You can call next( 'route' ), as said on the express api reference, application routing section:

Multiple callbacks may be given, all are treated equally, and behave just like middleware, with the one exception that these callbacks may invoke next('route') to bypass the remaining route callback(s).

Example

var express = require('express')
  , app = express()
;

// keep an eye on the function names
app.post( '/some/path', middleware1, middleware2, function route1( req, res, next ) {
      // write response
});
app.all( '*', function route2( req, res, next ) {
    // do something like handle a 404 request
});
app.use(function errorHandler( err, req, res, next ) {
    // handle error
});

function middleware1( req, res, next ) {
    // ...
    if ( !success ) {
        // bypasses middleware2 and route1, route2 will be called
        return next( 'route' );
    }
    // calls middleware2
    next();
}
// intentionally similar to middleware1 to keep things clear
function middleware2( req, res, next ) {
    if ( !success ) {
      // bypasses route1 and route2
      // errorHandler will be called with the error
      return next( Error( 'middleware 2 failed' ) );
    }
    // calls route1
    next();
}
laconbass
  • 17,080
  • 8
  • 46
  • 54
  • @ laconbass yeah, I read that section too, but wasn't quite sure waht to make of it. How can that be used to solve this problem - should I create an route called `/error` and call `next('/error');`? – bguiz Jun 21 '13 at 15:39
  • @bguiz added an example, I hope it helps – laconbass Jun 21 '13 at 15:59
4

A little more tinkering yielded the answer:

var express = require('express');
var server = express();
var mw1 = function(req, resp, next) {
  //do stuff
  if (success) {
    next();
  } else {
    resp.send(406, 'Invalid because of this');
    req.connection.destroy(); //without calling next()
  }
};
var mw2 = function(req, resp, next) {
  //do stuff
  if (success) {
    next();
  } else {
    resp.send(406, 'Invalid because of that');
    req.connection.destroy(); //without calling next()
  }
};
server.post('/some/path', [mw1, mw2], function(req, resp) {
  //write response
});

The trick was send a response: resp.send(406, 'Invalid because of this');

Just prior to destroying the connection: req.connection.destroy();

In fact not destroying the connection, I found to also work, in the general case.

(But was required in my specific case, and is out of the scope of this question.)

If the response has already been sent, then express does not automatically call next() for you, as it appeared to do otherwise.

bguiz
  • 27,371
  • 47
  • 154
  • 243
  • 1
    you could simply use `response.end()` and it's done. There is no need for `req.connection.destroy()`. – laconbass Jun 21 '13 at 18:15
  • @laconbass ah, this is true. My specific use case, if you are interested, was to terminate the request if the size of the data stream exceeds a sane threshold defined by me. To protect against this happening, I wanted to explicitly destroy the connection upon detection. Additionally, if I understand the express documentation correctly, express automatically calls `resp.end()` when you call `resp.send(...`. – bguiz Jun 22 '13 at 00:53
  • You should change the title of the question. This answers how to terminate the request, not how exit from the chain of route specific middleware. – laconbass Jun 24 '13 at 17:35
  • 1
    @laconbass the objective was to exit from a chain of middleware, and this was achieved by terminating the response... so I think the question is fine the way it is! Thanks for the input anyway! – bguiz Jun 27 '13 at 12:31
1

I was under the impression that if you neither call next() nor send a response in a route handling function, express just hangs. Also FWIW I haven't used an array, mine looks like server.post('/some/path', mw1, mw2, function(req, resp) {...

Anyway. One alternative might be to restructure your code so you only have a single handling function. Do you have a good reason for mw1 and mw2 being middleware instead of regular async functions your handler calls?

var express = require('express');
var server = express();

var mw1 = function(req, res, callback) {
  // do stuff with req/res if necessary but don't send a response
  if (success) {
    callback(null);
  } else {
    callback('Error');
  }
};

var mw2 = function(req, res, callback) {
  //do other stuff but don't send a response
  if (success) {
    callback(null);
  } else {
    callback('Error');
  }
};

function mwBoth(req, res){
  mw1(req, res, function(err){
    if(err){ return res.send(500) };
    mw2(req, res, function(err){
      if(err){ return res.send(500) };
      // neither had an error
      res.redirect('/some/other/path');
    });
  });
};

server.post('/some/path', mwBoth);
Plato
  • 10,812
  • 2
  • 41
  • 61
  • 1
    @ plato indeed, the array syntax is equivalent to the non-array syntax: `These callbacks may be passed within arrays as well, these arrays are simply flattened when passed` [see here](http://expressjs.com/api.html#app.VERB) – bguiz Jun 21 '13 at 15:41
  • @ plato I'm not sure why you have received a downvote, I think that yours is a perfectly valid response. However, I personally would like a "proper" solution, if you will, that doesn't deviate too much from the express framework, i.e. something that sticks to the principles of using `connect` and `next`, as I am fairly certain that the pattern that I am describing is not really that uncommon. – bguiz Jun 21 '13 at 15:45
  • 1
    @ plato, sorry to keep the comments coming, but yes, I agree with your first assertion. I am surprised that when one middleware function doesn't call `next()`, the subsequent middleware function still gets called. This appears to be either a bug in express, or me incorrectly interpreting express' documentation. – bguiz Jun 21 '13 at 15:48