Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error-handling middleware doesn't always work

I'm building a example website using Express and I hit something I don't quite understand.
Error-handling middleware(s) should be last one(s) in the pipeline, if I understand correctly. For example, works just fine:

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

var app = express();
app.set('view engine', 'jade');
app.set('views', './views');

app.use(express.static('./public'));
http.createServer(app).listen(portNumber, function() { });

app.get('/hello', function(req, res) {
    res.send('Welcome!');
});

app.use(function(err, req, res, next) {
    res.status(500).send('something broke!');
});

app.get('/error', function(req, res, next) {
    somethingNonExistent(2016);
});

However, if I register that middleware before http.createServer call, but after all other middlewares were registered, it won't work - my code isn't called:

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

var app = express();

app.use(express.static('./public'));

app.use(function(err, req, res, next) {
    res.status(500).send('something broke!');
});

http.createServer(app).listen(portNumber, function() { });

app.get('/hello', function(req, res) {
    res.send('Welcome!');
});

app.get('/error', function(req, res, next) {
    somethingNonExistent(2016);
});

What did I miss here? My guess is that app.get calls use some middleware internally and it gets messed up.
I use Express 3.2.6 and Node.js 0.10.29, if that makes any difference

like image 680
chester89 Avatar asked Jan 31 '16 11:01

chester89


1 Answers

When you define routes/middleware, the path you specify is used to see if it matches with incoming requests. Your request will always be routed to the first match. A request might have multiple matches, so order matters here. You can hit the next matching route/middleware by calling the next() function.

When you mount middleware using app.use without specifying a path, every path is eligible to hit that middleware. So, if it's the first thing you mount, every request will use that middleware.

If you want a catch all error handler, you'll want the opposite - you should mount middleware at the very end of your route definitions. You'll need to call the next function in your handler to actually reach this middleware:

app.get('/hello', function(req, res, next) {
  ...
  // Let's pretend that there was some error
  next()
});

// After all of your route definitions...
app.use(function(req, res) {
  res.status(500).send('something broke!');
})

Note that if no route exists for the current path, you will also hit this catch all middleware.

like image 165
Wex Avatar answered Nov 08 '22 01:11

Wex