ExpressJSのルーティングの自動化(Node.js)

肥大化とは別に、Expressのルーティングにもう一つ不満がある。
app.jsに以下の様に一々登録しないといけない事だ。

app.get('/', routes.index);
app.get('/foo', routes.get_foo);
app.get('/bar', routes.get_bar);

app.post('/foo', routes.post_foo);
app.post('/bar', routes.post_bar);

これでは新しいメソッドが追加されるたびに、記述を増やしていかなければならない。
という事で、昨日やった肥大化の解消を更に改良しつつ、ルーティングの自動化をしてみた。

app.js

var express = require('express')
  , http = require('http');

var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'hjs');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});


// ルーティング
var routes = [];
var fs = require('fs');
var files = fs.readdirSync('./routes');
for (i in files) {
	var ext = files[i].substr(files[i].lastIndexOf('.'), files[i].length);
	var name = files[i].substr(0, files[i].lastIndexOf('.'));
	if (ext === '.js') {
		routes[name] = require('./routes/' + name);
		for (j in routes[name]) {
			eval('var myfunc = routes[name].' + j);
			if (j.substr(0, 4) === 'get_') {
				var method = j.substr(4, j.length);
				app.get('/' + method, myfunc);
			} else if (j.substr(0, 5) === 'post_') {
				var method = j.substr(5, j.length);
				app.post('/' + method, myfunc);
			} else {
				app.get('/' + j, myfunc);
			}
		}
	}
}
app.get('/', routes['index'].index);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

上記でやっている事は、

  • routesディレクトリ内の.jsファイルを取得し、requireする
  • メソッド名が'get_ほにゃらら'ならば、'ほにゃらら'というリクエスト名でgetメソッドで登録
  • メソッド名が'post_ほにゃらら'ならば、'ほにゃらら'というリクエスト名でpostメソッドで登録
  • メソッド名がどちらでもなければ、そのままのリクエスト名でgetメソッドで登録

といった感じ。'/'にindexを割り当てるのは上記の条件に当てはまらないため。

readdirSync()を使っているが、'/'の部分をindex判定させればreaddir()でも書けるはず。
ただ、この処理は起動時だけだと思うので、どちらでも良いと思う。

/class/function/id/
/function/:num

な形にも対応させてみたいが、とりあえずここまで。