exit from express middleware with specific http status
Asked Answered
P

3

12

Hopefully this is a simple one, but I have some custom middleware which I want to return a 404 or 401 etc to the user and stop the propagation of other handlers etc.

I was expecting I could do something like:

function SomeMiddleware(req, res, next) {
   if(user.notRealOrSomething)
   { throw new HttpException(401, "Tough luck buddy"); }

   return next();
}

However cannot find any specific info about how is best to do this.

Peltry answered 2/8, 2013 at 12:55 Comment(0)
V
7

You are suppose to pass errors to the next() function.

function SomeMiddleware(req, res, next) {
   if(user.notRealOrSomething) {
    return next(throw new HttpException(401, "Tough luck buddy")); 
   }

   next();
}

Any argument you pass to next will be considered an error except 'route' which will skip to the next route.

When next is called with an error the error middleware will be execute.

function (err, req, res, next) {
  // err === your HttpException
}

Express.js will treat any middleware with 4 arguments as error middleware.

Error-handling middleware are defined just like regular middleware, however must be defined with an arity of 4, that is the signature (err, req, res, next):

All of this is pretty well documented at: http://expressjs.com/guide/error-handling.html

Villeneuve answered 2/8, 2013 at 17:53 Comment(1)
This answer was incorrect a decade ago and still is. It will work, but the explanation is misguided: the function as shown will not call next() in the error case at all, because it will instead throw the exception while evaluating the arguments and exit before it can call the next() function.Predisposition
N
3

Nothing prevents you from using the response object:

res.send(401, "Tough luck buddy");
next('invalid user');
Nyasaland answered 31/1, 2014 at 20:0 Comment(2)
This didn't work for me in express 4, it still returned a 500 status and seemed to ignore the res dataLimekiln
this works for me with this change res.status(401).send(); return next("invalid user");Pentastich
P
0

The code as written in the answer should work just fine, since the code is synchronous - that is, it's not async and it doesn't hand next() off to a callback anywhere. Per the Express.js docs (emphasis mine):

Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.

So in synchronous middlewares and handlers, just throw the error, Express will catch it and pass it to the error middleware.

For asynchronous middlewares, you need to pass the error object to the next() function without throwing it. The example from the docs applies just as well to middlewares as it does to route handlers (which are in fact also middleware), to adapt it to your example:

function SomeMiddleware(req, res, next) {
  user.checkRealOrSomethingAsync(isReal => {
    if(!isReal) {
      // Note: Pass the exception, do not throw it!
      next(new HttpException(401, "Tough luck buddy"))
    }
    next()
  })
}

Note that, as per the Express docs, this means you must catch any errors thrown in async middlewares or routes and pass them into next().

However! Starting in Express 5, you don't need to catch errors thrown within async methods, the error will be propagated as normal without you doing anything. express-async-errors package will patch Express.js 4 to work similarly. Note this only applies to errors at the "top level" of the async function - if you're mixing async and callbacks, you'll still have to catch errors inside the callbacks.

Predisposition answered 22/4, 2022 at 20:34 Comment(1)
you're missing a return after next(error)Punctilio

© 2022 - 2024 — McMap. All rights reserved.