読者です 読者をやめる 読者になる 読者になる

Carpe Diem

備忘録。https://github.com/jun06t

Expressでroutingの後にmiddlewareを置きたい

Node.js

概要

あるサーバ用のライブラリを使ったときにrouting後にmiddlewareを挟む必要が出てきました。
そのときに幾つかハマったことがあったので、それをまとめます。

環境

  • Node.js 6.6.0
  • Express 4.13.4

Express4のミドルウェアの流れ

まずはミドルウェアの実行順について検証します。

const app = require('express')();
let log = '';

app.use(function(req, res, next) {
  log = 'A'
  next();
});

app.use(function(req, res, next) {
  log += 'B'
  next(new Error("Error!"));
});

app.use(function(err, req, res) {
  log += 'C'
  res.send("response 1:" + log);
});

app.use(function(err) {
  log += 'D'
  res.send("response 2:" + log);
});

app.use(function(err, req, res, next) {
  log += 'E'
  res.send("response 3:" + log);
});

app.listen(3000);

結果

response 3:ABE

ポイント

今回のポイントは以下です。

  • next()が呼ばれると次のapp.use(function(req, res, next) {})に進む
  • next(err)が呼ばれると次のapp.use(function(err, req, res, next) {})に進む
  • 上2つの区別は引数の数に依る。next()は3つ以下。next(err)は4つのミドルウェアへ進む
  • throw errの場合もapp.use(function(err, req, res, next) {})に進む

これらを理解していると上記の結果になったことも納得がいきますね。

routingの後のmiddleware

先ほどの話を踏まえてroutingの後にmiddlewareを置きます。

NG

const app = require('express')();
let log = '';

app.use(function(req, res, next) {
  log = 'A'
  next();
});

app.get('/', function(req, res) {
  log += 'B'
  res.send("response 1:" + log);
});

app.use(function(req, res, next) {
  log += 'C'
  console.log(log)
  next()
});

app.use(function(err, req, res, next) {
  log += 'D'
  console.log(log)
});

app.listen(3000);

この結果は

response 1:AB

となるだけでconsole.logは出力されません。


OK

const app = require('express')();
let log = '';

app.use(function(req, res, next) {
  log = 'A'
  next();
});

app.get('/', function(req, res, next) {
  log += 'B'
  res.send("response 1:" + log);
  next();
});

app.use(function(req, res, next) {
  log += 'C'
  console.log(log)
  next()
});

app.use(function(err, req, res, next) {
  log += 'D'
  console.log(log)
});

app.listen(3000);

結果は

response 1:AB

console.logは

ABC

と表示されます。


ポイント

ここで注意する点は以下です。

  • routingの最後にnext()を呼ぶ
  • res.send()を呼んだのでレスポンスはすでに返している
  • 上記の理由から、後からレスポンスを変更することはできない

まとめ

next()next(err)の違い、レスポンスが返るタイミングに注意すると大丈夫ですね。

ソース