連載
» 2015年05月11日 05時00分 公開

MEANスタックで始めるWebアプリ開発入門(4):Node.jsのMVCフレームワーク「Express」における静的ファイル、ルーティング定義、セッション管理、エラー処理 (2/2)

[中村修太,クラスメソッド株式会社]
前のページへ 1|2       

【3】MemoryStoreやRedisを使ったセッション管理

 Webアプリであれば、ログイン認証などの用途でセッションを使用し、ユーザーごとにデータを管理することがあるでしょう。Expressではデフォルトでメモリ上(MemoryStore)にデータを保存する仕組みがあります。

 開発用であれば、このMemoryStoreで問題ありませんが、production環境でアプリを起動しようとすると「MemoryStoreはプロダクション用に設計されていません」といった警告が表示されます。また、複数サーバー(プロセス)におけるセッション情報の共有もMemoryStoreではできません。

 そのため、実際の環境では後述する「Redis」「MongoDB」を使用したセッションストアを使用することが多いと思います。

 ここでは基本的なMemoryStoreと、「Redis」を使用したRedis Storeを使用してみましょう。

MemoryStoreを使用する

 まずは、メモリ上でセッション情報を保存する、MemoryStoreを使用してみます。Express 4.x系ではセッション用モジュールは本体から分離されているので、npmでインストールする必要があります。

% npm install express-session

 モジュールをインストールしたら、app.jsを下記のように記述してください。

var express = require('express');
var session = require('express-session');
 
var app = express();
 
//セッション設定
app.use(session({
  secret : 'secretKey'
}));
 
//ルーティング設定
app.get('/memory-store-counter', function(req, res) {
  var session = req.session;
  if (session && session.count) {
    session.count++;
  } else {
    session.count = 1;
  }
  res.send('count is ' + session.count)
});
 
app.listen(3000);
console.log('Server running at http://localhost:3000/');

 express-sessionをrequireし、オプションを設定してsessionを読み込みます。secretはexpress-sessionで唯一必須のオプションです。これは、クッキーの署名に使用される値で、文字列か配列を指定できます。本番環境ではランダムかつ、ある程度の長さの値が推奨されています。

 ではアプリを起動して動作確認してみましょう。nodeコマンドでアプリを起動し、「http://localhost:3000/memory-store-counter」にアクセスしてみてください。リロードするたびにカウンターの値が増えていきます。

 ブラウザーの別ウインドーを立ち上げてアクセスしてみましょう。カウンターの値はウインドーごとに異なっていることが分かります。

Redisストアを使用する

 先ほども言ったように、本番環境でのMemoryStoreは推奨されていません。その代わりにRedisを使用してセッション情報を保存する方法があります。

 Redisとはインメモリ型のKVS(Key-Value Store)で、RDBよりも速度が優れているという特徴があります(※Redisについての解説はこちら)。

 Expressの場合、「connect-redis」というモジュールを使用することで、簡単にRedisを使用してセッション管理ができます。

 これにより、セッションの保存先がRedisになり、アプリのプロセスが落ちたとしてもセッション情報が保持されたり、複数サーバー環境でもセッション共有が可能になったりします。

 まずはセッションの保存先となるRedisをインストールしましょう。ダウンロードページから自分のプラットフォームにあったファイルをダウンロードしてインストールします。

 なお、Macの場合はHomebrewを使うこともできます。下記コマンドを入力すればインストールできます。

% brew update && brew install redis

 インストールできたらredis-serverコマンドでRedisを起動しておきます。

% redis-server

 次に、connect-redisモジュールをインストールします。

% npm install connect-redis

 app.jsを下記のように修正しましょう。

var express = require('express');
var session = require('express-session');
 
//sessionを元にRedis Storeを取得
var RedisStore = require('connect-redis')(session);
 
var app = express();
 
//セッション設定(Redis Store使用)
app.use(session({
  store: new RedisStore({}),
  secret : 'secretKey'
}));
 
//ルーティング設定
app.get('/redis-store-counter', function(req, res) {
  var session = req.session;
  if (session && session.count) {
    session.count++;
  } else {
    session.count = 1;
  }
  res.send('count is ' + session.count)
});
 
app.listen(3000);
console.log('Server running at http://localhost:3000/');

 express-sessionを元にRedisStoreを取得し、それをsessionのstoreに設定しています。これだけでMemoryStoreからRedisStoreへ切り替えられます。

 アプリを起動したら「http://localhost:3000/redis-store-counter」にアクセスしてみてください。先ほどと同じようにアクセスごとにカウントアップされますが、アプリを再起動しても以前の値が保持されています。

 なお、RedisStoreに渡すオプションについては、「connect-redis」をご確認ください。

【4】エラーをハンドリングする

 プログラムの処理中にエラーが発生した場合、発生したエラーをハンドリングして適切な処理を行わなければなりません。Expressでエラー処理を行うためには、ミドルウエアを使う感覚で、app.use関数を使ってエラーハンドリング用の関数を指定します。

 ただし、ミドルウエアとは違い、エラーハンドリング用関数定義は以下のようになります(ミドルウエア定義にはerrが含まれません)。

//エラーオブジェクト,リクエスト,レスポンス,nextオブジェクト
function(err, req, res, next)

 では実際にエラー処理を試してみましょう。app.jsを次のように記述します。「/sample-error」にアクセスすると、エラーがthrowされるようになっています。

var express = require('express');
var session = require('express-session');
var app = express();
 
//ルーティング設定
app.get('/sample-error', function(req, res) {
  //エラーをthrowする
  throw new Error("error occurred !!");
});
 
app.listen(3000);
console.log('Server running at http://localhost:3000/');

 「http://localhost:3000/sample-error」にアクセスしてみましょう。エラーがthrowされ、エラーメッセージが画面にそのまま表示されます。

 このままではエラー発生時の制御ができないので、次のようにコードを修正します。

・
・
//ルーティング設定
app.get('/sample-error', function(req, res, next) {
  try {
    //エラーをthrowする
    throw new Error("error occurred !!");
  } catch (e) {
    next(e);
  }
});
 
//エラーハンドリング用関数を設定
app.use(errorHandler);
 
//エラーハンドリング用関数定義
function errorHandler(err, req, res, next) {
  res.status(500);
  res.send(err.message)
}
・
・

 「/sample-error」アクセス時のルーティング設定におけるコールバックメソッドの引数を見てください。「next」という引数が追加されています。このコールバック関数にエラーオブジェクトを渡して実行することで、次に処理を渡すことができます。ここでは、エラーをcatchしたらnext関数にエラーオブジェクトを渡して次(errorHandler)に処理を渡しています。

※nextを引数なしで呼び出すと、次に一致するルーティング設定に処理が移りますが、引数付きでnextを呼び出すと、エラーハンドリング用の関数に処理が移ります。

 ルーティング定義の下で、エラーハンドリング用関数であるerrorHandlerを設定しています。ここに処理が来ると、HTTPステータスコード500でエラーオブジェクトのメッセージを返します。

 では、アプリを起動してアクセスしてみてください。画面にはエラーオブジェクトに設定したメッセージ「error occurred !!」が表示されます。

 また、複数のエラー処理をつなげることもできます。先ほどの処理ではエラーメッセージを表示させるだけでしたが、その間にログ出力を挟んでみましょう。

・
・
app.use(logHandler);
app.use(errorHandler);
 
//エラーログ出力用関数
function logHandler(err, req, res, next) {
  console.error("[" + new Date + "] " + err.toString());
  next(err);
}
 
//エラーハンドリング用関数
function errorHandler(err, req, res, next) {
  res.status(500);
  res.send(err.message)
}
・
・

 処理を追加するのは簡単で、「app.use(errorHandler);」の上にlogHandlerをuseで指定するだけです。このapp.useを使った順番にルーティング情報やエラー処理の探索が行われるので、app.useを実行する順番は重要です。

 エラー処理については公式サイトの下記ページも参考にしてください。

次回はMEANスタックの「M」の部分、MongoDBについて

 さて今回は、staticファイル/ルーティング定義/セッションの使用/エラー処理といった、実際の開発で使用する機能について紹介しました。これら以外にミドルウエアの定義方法など、便利な機能もまだまだあるのですが、そういったものについては今後の連載で随時紹介していきます。

 次回はMEANスタックの「M」の部分、MongoDBについて紹介する予定です。

著者紹介

中村修太(なかむら しゅうた)

中村修太

クラスメソッド勤務の新しもの好きプログラマーです。数年前に東京から山口県に引っ越し、現在もリモート勤務しています。最近の趣味は空手とぬか漬け作り。


前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。