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

かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Express 4.x エラーハンドリングにハマる。

引き続き、ドットインストールのExpress入門 をやっています。
サンプルはExpress3系ですが、Express4系でチャレンジしています。今回は最後の #21 エラー処理をしていこう 値が正しくない時などエラーページを表示させる方法のレッスンでハマりました。

サンプルでは、app.use(app.router);の下、Routingの上に、エラーハンドリングを行う下記の処理を記述するようにあります。app.useだからエラーハンドリングもミドルウェアって認識で良いのでしょうかね?

// error
app.use(function(err, req, res, next) {
  res.send(err.message);
});

Express 4.xではapp.routerは廃止されているので、Routingの上、色いろあるミドルウェアの最後に同じコードを追加しました。

// app.js
var express        = require('express'),
    logger         = require('morgan'),
    bodyParser     = require('body-parser'),
    connect        = require('connect'),
    methodOverride = require('method-override'),
    expressSession = require('express-session'),
    cookieParser   = require('cookie-parser'),
    csrf           = require('csurf'),
    app            = express(),
    post           = require('./routes/post');

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// middleware
app.use(logger('dev'));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride(function(req, res){
  if (req.body && typeof req.body === 'object' && '_method' in req.body) {
    // look in urlencoded POST bodies and delete it
    var method = req.body._method;
    delete req.body._method;
    return method;
  }
}));

// csrf対策
app.use(cookieParser());
app.use(expressSession({secret: 'secret_key'}));
app.use(csrf());
app.use(function(req, res, next) {
  var token = req.csrfToken();
  res.locals.csrftoken = token;
  next();
});

// error
app.use(function(err, req, res, next) {
  res.send(err.message);
});

// Routing
app.get('/', post.index);
app.get('/posts/:id([0-9]+)', post.show);
app.get('/posts/new', post.new);
app.post('/posts/create', post.create);
app.get('/posts/:id([0-9]+)/edit', post.edit);
app.put('/posts/:id([0-9]+)', post.update);
app.delete('/posts/:id([0-9]+)', post.destroy);

app.listen(3000);

サンプルのとおりだど、next( new Error('Error Message') )で渡されたエラーメッセージだけが表示されるはずなのですが、、、

Error: ID not valid
   at exports.update (/express/blog/routes/post.js:25:15)
   at Layer.handle [as handle_request] (/express/blog/node_modules/express/lib/router/layer.js:95:5)
   at next (/express/blog/node_modules/express/lib/router/route.js:131:13)
   at Route.dispatch (/express/blog/node_modules/express/lib/router/route.js:112:3)
   at Layer.handle [as handle_request] (/express/blog/node_modules/express/lib/router/layer.js:95:5)
   at /express/blog/node_modules/express/lib/router/index.js:277:22
   at param (/express/blog/node_modules/express/lib/router/index.js:349:14)
   at param (/express/blog/node_modules/express/lib/router/index.js:365:14)
   at Function.process_params (/express/blog/node_modules/express/lib/router/index.js:410:3)
   at next (/express/blog/node_modules/express/lib/router/index.js:271:10)

はい。エラーがそのまま表示されちゃっています。
methodOverrideとcsrf対策の処理もExpress 4.xに合わせるために変更しているので、これらの影響があるのかもと思い、書く処理にconsole.logを仕込んでみました。その結果

GET /posts/2/edit 200 6.993 ms - 604
>> methodOverride
>> csrftoken
>> next( new Error('ID not valid') )
Error: ID not valid

と表示されましたので、methodOverrideとcsrf対策の処理は問題なく行われており、next( new Error() )がエラーハンドリングの処理に行かずにエラーになっていると解りました。

エラーハンドリングの処理を書く位置が悪かった。

色々と検索してみたのですが、コレといった解決策を見つけることができず、expressコマンドで自動生成できるapp.jsを参考に見てみることにしました。どうやらRoutingの指定より後にエラーハンドリングを書いてるっぽい!
ということで、エラーハンドリングの記述をRoutingより後に移動させてみました。

// app.js
var express        = require('express'),
    logger         = require('morgan'),
    bodyParser     = require('body-parser'),
    connect        = require('connect'),
    methodOverride = require('method-override'),
    expressSession = require('express-session'),
    cookieParser   = require('cookie-parser'),
    csrf           = require('csurf'),
    app            = express(),
    post           = require('./routes/post');

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// middleware
app.use(logger('dev'));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride(function(req, res){
  if (req.body && typeof req.body === 'object' && '_method' in req.body) {
    // look in urlencoded POST bodies and delete it
    var method = req.body._method;
    delete req.body._method;
    return method;
  }
}));

// csrf対策
app.use(cookieParser());
app.use(expressSession({secret: 'secret_key'}));
app.use(csrf());
app.use(function(req, res, next) {
  var token = req.csrfToken();
  res.locals.csrftoken = token;
  next();
});

// Routing
app.get('/', post.index);
app.get('/posts/:id([0-9]+)', post.show);
app.get('/posts/new', post.new);
app.post('/posts/create', post.create);
app.get('/posts/:id([0-9]+)/edit', post.edit);
app.put('/posts/:id([0-9]+)', post.update);
app.delete('/posts/:id([0-9]+)', post.destroy);

// error
app.use(function(err, req, res, next) {
  res.send(err.message);
});

app.listen(3000);

これで、試してみたところ意図したとおりに画面にはエラーメッセージだけが表示されるようになりました。  


バイナリ畑でつかまえて

バイナリ畑でつかまえて