設計編からだいぶ間があいてしまったが、routes/auth.js
に枠組みだけ作ったauth
モジュールの実装を埋めていこう。
スタート地点はこの段階。
module.exports = function(models) { var User = models.user; return { signup: function(req, res) { } , login: function(req, res) { } , logout: function(req, res) { } }; };
サインアップ
signup
メソッドの役割は新規ユーザーを登録すること。リクエストボディからusername
とpassword
の2つのパラメータを読み取って、User
モデルを介して DBにレコードを追加する。
signup: function(req, res) { var username = req.body.username || null , password = req.body.password || null; if(!username || !password) { res.statusCode = 403; res.end({ error: 'Shortage of params' }); return; } User.create({ username: username , password: User.hashPassword(password) }) .success(function(user) { user = user.values; delete user.password; res.end(user); }) .error(function(error) { res.statusCode = 500; res.end({ error: error }); }); }
11-14行目:DBにレコードを追加するにはモデルのcreate
メソッドを使う。引数は新しいレコードのデータを表すオブジェクトリテラル。リクエストボディから読み取ったpassword
は平文なので、User
モデルのクラスメソッドとして実装したhashPassword
メソッドを使ってハッシュ値に変換している。
15-19行目:レコードの追加が成功するとsuccess
メソッドに渡した関数がコールバックされ、追加されたモデルのインスタンスが引数に渡される。追加されたレコードにアクセスするにはvalues
プロパティを使う。例えば、自動連番のインデックスや、現在時刻がデフォルト値になっている時刻データのように、追加時に確定するようなデータもvalues
プロパティには含まれている。password
カラムはハッシュ化されたパスワードだが、外部に漏らすことはないので、クライアントには送信しないようにしている。
20-23行目:error
メソッドにはレコードの追加に失敗した場合のコールバック(エラーハンドラ)を渡す。
レコードの追加はcreate
メソッドを使う以外に、build
メソッドとsave
メソッドを別々に呼び出す方法もある。データの追加前にバリデーションを行うのがよくあるケースだろう。例えば、username
カラムをメールアドレス形式にするように仕様を変えたとした場合、User
モデルの定義時にバリデーションも含めるようにする。
// models/user.js module.exports = function(sequelize, DataTypes) { return sequelize.define('user', { username: { type: DataTypes.STRING , unique: true , validate: { isEmail: true } } , password: 'char(40)' }); };
データ追加前にバリデーションを行うには、先にbuild
メソッドでモデルのインスタンスを生成してvalidate
メソッドを呼び出す。
var new_user = User.build({ username: username , password: User.hashPassword(password) }) , invalid = new_user.validate(); if(invalid) { res.statusCode = 403; res.end({ error: 'Invalid param', detail: invalid }); return; } new_user.save() .success(function(user) { ... }) .error(function(error) { ... });
validate
メソッドは、モデル定義のvalidate
で指定したルールを使ってバリデーションを行い、何もエラーがなければnull
が返ってくる。Sequelizeに標準で組み込まれているバリデータがたくさんあるので見ておくといいだろう。
すでに設計編でルーティングは実装しているので、curl
を使ってサインアップを実行してみよう。
$ curl -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/signup
ログイン
login
メソッドの役割はユーザーを認証すること。リクエストボディからusername
とpassword
の2つのパラメータを読み取って、User
モデルを介して DBからレコードを検索する。
login: function(req, res) { var username = req.body.username || null , password = req.body.password || null; if(!username || !password) { res.statusCode = 403; res.end({ error: 'Shortage of params' }); return; } User.find({ where: { username: username , password: User.hashPassword(password) } }) .success(function(user) { if(user) { user = user.values; delete user.password; req.session.user_id = user.id; res.send(user); } else { res.statusCode = 403; res.end({ error: 'Authentication failure' }); } }) .error(function(error) { res.statusCode = 500; res.end({ error: error }); }); }
11-16行目:1件のレコードを検索するにはfind
メソッドを使う。引数には検索(SELECT
文)の条件などを渡すことができる。今回はユーザー認証が目的なので、ユーザー名とパスワードのハッシュ値をWHERE
句に指定して検索している。複数件のレコードを検索する場合は、findAll
(またはall
)メソッドを使う。
18-23行目:検索を実行した結果、レコードが見つかった場合は、該当レコードのインスタンスがコールバック関数の引数に渡ってくる。サインアップで使ったcreate
と同様にデータへのアクセスはvalues
プロパティを使う。レコードが見つかったということは、ユーザー名とパスワードが一致した証なので、セッションにid
カラムを保存しておくようにする。
24-27行目:検索の結果、レコードが見つからなかった場合もsuccess
メソッドに渡したコールバック関数が呼び出されるが、引数はnull
になっている。error
メソッド側のコールバックが呼ばれるわけではないので注意。
先ほどサインアップしたアカウントを使って、curl
からログインを実行してみる。セッション管理にはCookieが使われているので、-c
オプションでCookieJarに保存するようにしておこう。
$ curl -c ~/.cookies -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/login
ログアウト
logout
メソッドの役割は既存のログインセッションを破棄すること。サインアップ、ログインに比べると、DBアクセスもないので簡素な実装になる。
logout: function(req, res) { req.session.destroy(); res.end(); }
curl
でのログアウト実行時は、ログイン時と同じCookieJarを-b
オプションで指定する。
$ curl -b ~/.cookies -X POST localhost:3000/auth/logout
1 コメント