express は、app.use()
や app.VERB()
などを使って、
URLのパスに合わせて、必要なミドルウェアを設定していくが、
途中でエラーが起きた場合にcallback関数の第1引数に
next(new Error("Error"))
とエラーを入れるようになっている。
ただ、エラーが起きた後に設定したミドルウェアの引数の数によって挙動が変わる。
var app = require('express')();
var text = '';
app.use(function(req, res, next) {
text = 'A'
next();
});
app.use(function(req, res, next) {
text += 'B'
next(new Error("Error!"));
});
app.use(function(err, req, res) {
text += 'C'
res.send("RESULT 1:" + text);
});
app.use(function(err) {
text += 'D'
res.send("RESULT 2:" + text);
});
app.use(function(err, req, res, next) {
text += 'E'
res.send("Result 3:" + text);
});
app.listen(3000);
この場合、httpアクセスをした場合
結果は Result 3:ABE
になる。
なぜ実行されない関数があるのか
これは、途中でエラーが発生した場合に、このミドルウェアのcallbackの挙動が変わるため。
ミドルウェアは、Layer
というlayerオブジェクトを積み重ねて、
httpリクエスト毎に処理が必要かどうかをチェックをしている。
proto.use = function(route, fn){
// 省略…
var layer = new Layer(route, {
sensitive: this.caseSensitive,
strict: this.strict,
end: false
}, fn);
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push(layer);
Layer
のコンストラクタはこんな感じ。
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %s', path);
options = options || {};
this.regexp = pathRegexp(path, this.keys = [], options);
this.handle = fn;
}
fn
が、app.use()
などで慣例的に req
や res
や next
を渡す関数を指していて、layer.handle
に設定されている。
この関数がパスなどのマッチングに合わせて、順番に実行されていく。
実際にこれが実行されるのが以下になる。
var arity = layer.handle.length;
if (err) {
if (arity === 4) {
layer.handle(err, req, res, next);
} else {
next(err);
}
} else if (arity < 4) {
layer.handle(req, res, next);
} else {
next(err);
}
err
が入っている場合は、引数が4つでなければ、layer.handle()
が呼ばれずにnext(err)
が呼び出される。
なので、最初の例では C
と D
は呼び出されない。