設計編からだいぶ間があいてしまったが、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 コメント