expressの使い方を逆引き形式でまとめてみた。参考にしたのは2014年2月20日時点(express 3.4.8時点)の公式ドキュメントおよびソースコード、サンプルなど。
ルーティングを設定する
- ルーティングを設定するには(i.e. パスとコントローラ関数をマッピングするには)
app.get(path, func)
やapp.post(path, func)
など各HTTP Verbに対応した関数を使う - ルーティング設定は書かれた順番に評価され、リクエストと
path
の一致したものが適用される -
path
は文字列または正規表現を指定可能 -
func
のシグネチャはfunction(req, res[, next])
-
next
はコールバック関数- 引数無し
next()
で呼び出すと、次に一致するルーティング設定に処理が移る - 引数有り
next(object)
で呼び出すと、エラーハンドリング用の関数に処理が移る(エラーハンドリングについては別項目参照のこと)
- 引数無し
app.get('path_to_somewhere', function(req, res) {
:
// something
:
});
app.post(/^\/items\/[0-9]+\.html$/, function(req, res) {
:
// something
:
});
/*
app.get()、app.post()以外のルーティング設定関数は以下の通り。それぞれ同名のHTTP Verbに対応している。
app.put()
app.head()
app.delete()
app.options()
app.trace()
app.copy()
app.lock()
app.mkcol()
app.move()
app.propfind()
app.proppatch()
app.unlock()
app.report()
app.mkactivity()
app.checkout()
app.merge()
app.m-search()
app.notify()
app.subscribe()
app.unsubscribe()
app.patch()
app.search()
*/
テンプレートをレンダリングする
- テンプレートをレンダリングするには
response.render(templateName)
を使う -
templateName
はテンプレートファイルの名称であり、app.set('views', path)
で指定したパス直下に置く必要がある -
templateName
では拡張子を省略可能。省略した場合app.set('view engine', ext)
で指定したデフォルト拡張子であると解釈される - レンダリングに利用するテンプレートエンジンは
app.engine(ext, engineFunc)
で登録する
// テンプレートファイルを置くパスを設定。デフォルトは "process.cwd() + '/views'"
app.set('views', 'path_to_somewhere');
// レンダリングに利用するテンプレートエンジンを登録
var jadeEngine = require('jade').someFunc;
app.engine('jade', jadeEngine);
var ejsEngine = require('ejs').someFunc;
app.engine('ejs', ejsEngine);
/*
app.engine()の第二引数はfunction(path, options, callback)のシグネチャを持つ必要がある。consolidate.js(https://github.com/visionmedia/consolidate.js)を使うと様々なテンプレートエンジンのシグネチャをこの形式に合わせてくれるので便利。
*/
// templateNameで拡張子が省略された場合のデフォルト拡張子
app.set('view engine', 'ejs');
//コントローラーの処理
app.get('path_to_somewhere', function(req, res) {
:
// レンダリング実行
res.render('index'); //この例の場合index.ejsファイルとして解釈される
:
});
テンプレートに値を埋め込む
- テンプレートに値を埋め込む方法は4つ
-
response.render(templateName, object)
のobject
のプロパティで指定 -
response.locals.name = value
で指定 -
app.locals.name = value
で指定 -
app.set(name, value)
で指定
-
- 1〜3の方法で埋め込んだ値は、テンプレートからはグローバルオブジェクトのプロパティとして参照可能
- 4の方法で埋め込んだ値は、テンプレートからは
settings
オブジェクトのプロパティとして参照可能
app.get('path_to_somewhere', function(req, res) {
:
// 方法1
res.render('index', {title1: 'exposed via res.render'});
:
});
app.get('path_to_somewhere', function(req, res) {
:
// 方法2
res.locals.title2 = 'exposed via res.local');
res.render('index');
:
});
// 方法3
app.locals.title3 = 'exposed via app.local');
// 方法4
app.set('title3', 'exposed via app.set');
テンプレート側(ejsの例)
<h1> <%= test1 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 -->
<h1> <%= test2 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 -->
<h1> <%= test3 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 -->
<h1> <%= settings.test3 %> </h1> <!-- settingsオブジェクトのプロパティとして参照可能 -->
<!-- 以下のように出力される
<h1>exposed via res.render</h1>
<h1>exposed via res.local</h1>
<h1>exposed via app.local</h1>
<h1>exposed via app.set</h1>
-->
レスポンスのヘッダーを指定する
- レスポンスのヘッダーを指定するには
response.set(name, value)
を使う - 複数のヘッダーをまとめて指定する場合は
response.set
の引数にオブジェクトを渡す -
response.set
の別名としてresponse.header
を使っても良い(名前が違うだけで機能は全く同じ)
app.get('path_to_somewhere', function(req, res) {
:
res.set('Cache-Control': 'max-age=600');
res.set('ETag': '12345');
:
});
app.get('path_to_somewhere', function(req, res) {
:
// オブジェクトを引数に渡して複数同時に指定
res.set({
'Cache-Control': 'max-age=600',
'ETag': '12345'
});
:
});
レスポンスのステータスコードを指定する
- レスポンスのステータスコードを指定するには
response.status(code)
を使う
app.get('path_to_somewhere', function(req, res) {
:
res.status(501);
:
});
レスポンスにCookieを含める
- レスポンスにCookieを含めるには
response.cookie(name, value[, option])
を使う -
value
には値だけでなくオブジェクトも指定可能(JSON化してシリアライズされる) -
option
には以下の値を指定できる- domain: Cookieを有効とするドメイン(初期値:ホストのドメイン)
- path: Cookieを有効とするパス(初期値:'/')
- httpOnly: HTTPだけで有効にするか(初期値:false)
- maxAge: 破棄されるまでの時間(ミリ秒)。このオプションはexpire属性のシンタックスシュガー(初期値:session)
- secure: Cookieに署名するか(初期値:false)
-
secure
オプションをtrue
にする場合は、cookieParserミドルウエアを読み込んで、署名用の鍵を指定する必要がある(cookieParserミドルウエアの読み込みは別項目参照のこと)
app.get('path_to_somewhere', function(req, res) {
:
res.cookie('cookie_name', 'cookie_value', {maxAge: 900000, httpOnly: true});
:
});
レスポンスからCookieを取り除く
- レスポンスからCookieを取り除くには
response.clearCookie(name)
を使う
app.get('path_to_somewhere', function(req, res) {
:
res.clearCookie('cookie_name');
:
});
リダイレクトする
- リダイレクトするには
response.redirect([status, ] url)
を使う -
status
を省略した場合は302(Found)が使われる
app.get('path_to_somewhere', function(req, res) {
:
res.redirect(302, "../login");
:
});
ミドルウエアを使う
- ミドルウエアとはexpressにプラグインして使う機能のことで、connectフレームワークを基盤としている
- expressに同梱されているミドルウエアはexpressのドキュメント、またはconnectのドキュメントを参照のこと
- 利用するミドルウエアは
app.use([path, ] middlewareFunc)
で読み込む -
path
を指定した場合、リクエストとpath
が一致した場合のみそのミドルウエアが利用される。path
が省略された場合は'/'
として扱われる - ミドルウエアの実態は
middlewareFunc
関数で、そのシグネチャはfunction(req, res, next)
- ミドルウエアは
app.use
された順番に適用される(ルーティング設定の適用順序と同じ考え方)
app.use(middlewareFactory(someOption));
/*
ミドルウエアはファクトリ関数(関数をreturnする関数)を使って生成するものが多い。この例の場合、middlewareFactory関数はfunction(req, res, next)というシグネチャを持った関数を返す
*/
staticファイルを公開する
- 画像ファイルやCSSファイル、JavaScriptファイルなどのstaticファイルを公開するには、staticミドルウエアを利用する
- staticミドルウエアのファクトリ関数は
express.static(path[, option])
-
path
には、staticファイルを公開したいフォルダ / ファイルへのパスを指定 -
option
には以下の値を指定できる- maxAge: Browser cache maxAge in milliseconds. defaults to 0
- hidden: Allow transfer of hidden files. defaults to false
- redirect: Redirect to trailing "/" when the pathname is a dir. defaults to true
- index: Default file name, defaults to 'index.html'
app.use(express.static('path_to_somewhere', {hidden: true}));
リクエストに含まれるCookieを読み出す
- リクエストに含まれるCookieを読み出すには、cookieParserミドルウエアを使う(このミドルウエアは将来的にdeprecatedになると予告されており、cookiesミドルウエアに置き換わる予定)
- cookieParserを使うと、リクエストに含まれるCookieの情報が
request.cookies[]
メンバに格納される - cookieParserミドルウエアのファクトリ関数は
express.cookieParser([secret])
-
secret
には署名付きCookie用の鍵を指定する
app.use(express.cookieParser('some_secret'));
セッションを使う
- セッション機能を使うには、sessionミドルウエアを利用する
- cookieを利用してセッションIDを管理するのでcookieParserミドルウエアも必須
- sessionミドルウエアを使うと、セッション情報を保持するオブジェクト
request.session
が生成される -
request.session
オブジェクトにはセッションをリセットしたり破棄するための関数が用意されている。詳細はconnectのドキュメントを参照のこと - sessionミドルウエアのファクトリ関数は
express.session([option])
-
option
には以下の値を指定できる- key: cookie name defaulting to connect.sid
- store: session store instance
- secret: session cookie is signed with this secret to prevent tampering
- cookie: session cookie settings, defaulting to { path: '/', httpOnly: true, maxAge: null }
- proxy: trust the reverse proxy when setting secure cookies (via "x-forwarded-proto")
// ミドルウエアの読み込み
app.use(express.cookieParser());
app.use(express.session({secret: 'secret_for_sign'}));
// request.sessionオブジェクトを利用する(この例では訪問回数を記録している)
app.get('path_to_somewhere', function(req, res) {
if (req.session.counter) {
res.end('you already visit this page ' + req.session.counter + '-th time');
req.session.counter++;
}
else {
req.session.counter = 1;
res.end('this is first time you visit this page!');
}
});
エラーを発生させる
- コントローラー関数の中でエラーを発生させるには、例外をthrowする、または
next()
コールバックの引数に例外オブジェクトを渡す
// 例外をthrowする
app.get('path_to_somewhere', function(req, res) {
:
throw new Error('this is error');
});
// next()コールバックを呼び出す
app.get('path_to_somewhere', function(req, res) {
:
next(new Error('this is error');
});
エラーをハンドリングする
- エラーをハンドリングするには、
app.use()
を使ってエラーハンドリング用関数を指定する(つまりエラーハンドリングもミドルウエアと同じ扱い) - エラーハンドリング用関数のシグネチャは
function(err, req, res, next)
でなければならない(その他ミドルウエアとはシグネチャが異なる。ミドルウエアにはerr
が含まれない) - エラーハンドリング用関数の中で
next()
を引数なしで呼び出した場合は、その他のミドルウエアに処理が移る -
next()
をerr引数付きで呼び出した場合は、その他のエラーハンドリング用関数だけが実行される(その他のミドルウエアは実行されない)
app.use(function(err, req, res, next) {
:
next(err); // もう一つのエラーハンドリングコールバックに処理を渡す
});
app.use(function(err, req, res, next) {
:
res.status(500);
res.render('500');
});
CSRF対策する
- csrfミドルウエアを使って以下を行う
- トークンを生成してセッションに保存(csrfミドルウエアが自動で行う)
- Formをレンダリングする際にトークンを埋め込む(要実装)
- FormからのPOSTに含まれるトークンとセッションに保存されたトークンを比較し、異なっている場合はエラーを発生させる(csrfミドルウエアが自動で行う)
- エラーを適切にハンドリングする(要実装、詳細は別項目参照のこと)
- セッションを利用するのでcookieParserミドルウエアとsessionミドルウエアも必須
- csrfミドルウエアのファクトリ関数は
express.csrf()
// sessionミドルウエアの読み込み
app.use(express.cookieParser());
app.use(express.session({secret: 'secret_to_sign'}));
// csrfミドルウエアの読み込み
app.use(express.csrf());
// エラーハンドリングの実装
app.use(function(err, req, res, next) {
:
// something
:
});
// ルーティング設定
app.get('path_to_form', function(req, res, next) {
:
// セッションに保存されたtokenをFormに埋め込む
res.local.token = req.session._csrf;
res.render('index');
});
View側(ejsの例)
<form action='/form', method>
<input type='hidden' name='_csrf' value='<%=token%>'/>
</form>
アクセスログ / エラーログを残す
- アクセスログやエラーログを残すには、loggerミドルウエアを利用する
- loggerミドルウエアのファクトリ関数は
express.logger([string | function | option])
- 引数で出力フォーマットや出力先を指定できる。詳細はloggerミドルウエアのマニュアルを参照のこと
- 引数を省略した場合は、Apacheと同等の出力フォーマットでstdoutに出力される
app.use(express.logger());
ルーティングとミドルウエアの実行順序を明示する
- 原則として、ミドルウエアやルーティング設定はソースコードに書かれた順番に実行される
- ただし、ルーティング設定(
app.get()
やapp.post()
)は、express内部で一つにまとめられ、一つのミドルウエアのように扱われる(ここではルーティングミドルウエアと呼ぶ) - このルーティングミドルウエアが実行されるのは、最初のルーティング設定が書かれた箇所となる(以下の例参照)
// ①〜⑥の順序で実行される
// 最初のルーティング設定
// この位置にルーティングミドルウエアが入る
app.get('path_to_somewhere', function(req, res) { // ①
:
});
app.get('path_to_somewhere', function(req, res) { // ②
:
});
app.use(someMiddleware); // ⑤
app.use(otherMiddleware); // ⑥
app.get('path_to_somewhere', function(req, res) { // ③
:
});
app.get('path_to_somewhere', function(req, res) { // ④
:
});
- このままではソースコード全体を見なければ実行順序が分からない(特に大規模なシステムの場合)
- そこで、実行順序を見通し良く記述するために
app.use(app.router)
を使うとよい -
app.use(app.router)
はルーティングミドルウエアそのものであり、これが書かれた位置にルーティングミドルウエアが入ることになる
// ①〜⑥の順序で適用される
app.get('path_to_somewhere', function(req, res) { // ③
:
});
app.get('path_to_somewhere', function(req, res) { // ④
:
});
//
// 全体を見なくてもここだけ見れば実行順序が判る
//
app.use(someMiddleware); // ①
app.use(otherMiddleware); // ②
app.use(app.router); // この位置にルーティングミドルウエアが入る
app.get('path_to_somewhere', function(req, res) { // ⑤
:
});
app.get('path_to_somewhere', function(req, res) { // ⑥
:
});
全てのHTTP Verbに一致するルーティングを指定する
- HTTP Verb毎のルーティング設定は前述の通りだが、全てのVerbに対して適用するルーティング設定は
app.all()
で行う
app.all('path_to_somewhere', function(req, res, next) {
:
});
コンテンツをgzip / deflatで圧縮して返す
- compressミドルウエアを利用する
app.use(express.compress());
ディレクトリをリスティングする
- ディレクトリをリスティングするには、directoryミドルウエア利用する
- direcotoryミドルウエアのファクトリ関数は
express.directory(path [,option])
-
path
には、directoryを公開したいフォルダへのパスを指定する -
option
には以下の値を指定できる- hidden: display hidden (dot) files. Defaults to false.
- icons: display icons. Defaults to false.
- filter: Apply this filter function to files. Defaults to false.
app.use(express.directory('path_to_somewhere'), {hidden: false});
faviconを指定する
- faviconがstaticファイルとして公開されていれば何もしなくてもよい
- ただし、faviconへのアクセスをログに残したくない場合は、faviconミドルウエアを利用して以下のようにするとよい
// 先にfaviconミドルウエアを適用
app.use(express.favicon('path_to_favicon'));
// その後loggerミドルウエアを適用
app.use(express.logger());
デバッグ関連
ルーティング設定を確認する
- ルーティングの設定は
app.routes
オブジェクトに格納されている - このオブジェクトを
console.log
に出力すれば、登録されたルーティング設定を確認できる
console.log(app.routes);
/* 出力の例
{ get:
[ { path: '/',
method: 'get',
callbacks: [Object],
keys: [],
regexp: /^\/\/?$/i },
{ path: '/user/:id',
method: 'get',
callbacks: [Object],
keys: [{ name: 'id', optional: false }],
regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ],
delete:
[ { path: '/user/:id',
method: 'delete',
callbacks: [Object],
keys: [Object],
regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ] }
*/
express内部のデバッグログを出力する
-
DEBUG
環境変数にexpress:*
を設定した上でnodeを起動すれば、デバッグログが出力される
$ DEBUG=express:* node app.js
# 出力の例
# express:application booting in development mode +0ms
# express:router defined get /hello.txt +0ms
# express:router defined get /hello.txt +1ms