Reputation: 8617
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
Upvotes: 8
Views: 2694
Reputation: 12033
Docs
http://expressjs.com/en/guide/error-handling.html
You define error-handling middleware last, after other app.use() and routes calls; for example:
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
// logic
});
The Internal
For ease of understanding, you can imagine the pipeline. When your controller receives a request, inside Express, it looks like this
try {
fn(req, res, next); // your controller
} catch (err) {
next(err);
}
So, then your code throw error, it will call next
with err
. Basically, it's the same as call next(new Error())
. After that, express trying to find next middleware with 4 arguments in middleware pipeline. In your situation error handler declared before your shady controller, so he doesn't involved.
Technically, there is no difference between the controller and middlewares. Optionally you can pass in the controller next
parameter , and call it to pass through the pipeline further. If you don't call next()
, you finish processing the request.
Upvotes: 1
Reputation: 15715
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.
Upvotes: 4