5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

express4でErrorが起きた後の制御の話

Posted at

express は、app.use()app.VERB() などを使って、
URLのパスに合わせて、必要なミドルウェアを設定していくが、
途中でエラーが起きた場合にcallback関数の第1引数に
next(new Error("Error")) とエラーを入れるようになっている。

ただ、エラーが起きた後に設定したミドルウェアの引数の数によって挙動が変わる。

app.js
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リクエスト毎に処理が必要かどうかをチェックをしている。

https://github.com/visionmedia/express/blob/4.4.1/lib/router/index.js#L369
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 のコンストラクタはこんな感じ。

https://github.com/visionmedia/express/blob/4.4.1/lib/router/layer.js#L14
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() などで慣例的に reqresnext を渡す関数を指していて、layer.handle に設定されている。

この関数がパスなどのマッチングに合わせて、順番に実行されていく。
実際にこれが実行されるのが以下になる。

https://github.com/visionmedia/express/blob/4.4.1/lib/router/index.js#L247
      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) が呼び出される。
なので、最初の例では CD は呼び出されない。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?