NodeでRedisでExpressでSessionをはってやるまで
開発環境を整える
まともなデバッグ環境がないと厳しい
node-dev
ファイルの更新があるたびにサーバーを再起動してくれる
npm install -g node-dev
node-inspector
npm install -g node-inspector
起動するときはこんな感じ
#バックグラウンドで起動っぽい感じ node-inspector & #--debugでdebugモード node-dev --debug app.js
ただしこれでせっかくnode-inspectorしながらnode-dev --debugしているのに肝心のファイルを上書きして更新してやるとnode-inspectorがユーザーの作成したjsファイルを拾ってくれない→ 結局再起動になる。とかそうしてもnode-inspectorがファイルを拾ってくれないときがけっこうがるんだけど、これ、どうにかなんないかな?
しかし実際ブレークポイント打って変数を確認したりコンソールつかえるのは大変に便利、というかそれができないといろいろと話にならない。
package.jsonとnpm install
もろもろを含めた今のところのpackage.jsonはこんな感じ
{ "author": "atasatamatara", "name": "chat_proj", "description": "kusoga", "version": "0.0.0", "repository": { "url": "" }, "dependencies": { "express": "*", "jade": "*", "socket.io": "*", "redis" : "*", "connect-redis": "*" }, "devDependencies": {}, "optionalDependencies": {}, "engines": { "node": "*" } }
Express, jade, sokect.io(あとで使う予定), redis, connect-redisですね。とりあえず最新版をインストール。package.jsonを作るのは
npm init
これで対話的に作れます。dependenciesのところだけ適宜付け加える感じ。で、インストール
npm install
個別に指定してもいいですけど、package.jsonからやったほうがなにかといい気がする。
Expressの土台
globalじゃなくてlocalにインストールしたのでそっちのpathで
~/chat_proj/node_modules/express/bin/express #のちにsessionを使うのだから、こうしたほうがよかったかも ./bin/express --session chat_app #~/chat_proj/node_modules/express/bin/express chat_app
もろもろが作成される。カレントに展開されるから適宜移動させる
Redisのインストール
普通にaptで
sudo apt-get install redis
起動は redis-server (redis.conf), 対話的クライアントは redis-cli で。ちょっと軽く試したりしました
RedisでExpressのセッションを貼ってやる
これが本題。とりあえず app.js はこんな感じにしてやった
var express = require('express'), routes = require('./routes'), //ここを追記。Express + Redis なのでこのような書き方 //なんかよくわからんがconnect-redisが必要で、ラクらしい RedisStore = require('connect-redis')(express); var app = module.exports = express.createServer(); // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); //sessionを扱うときはrouterの前にこれを書かなければならない //new RedisStore ってサンプルにあるけど普通 new RedisStore() じゃね? app.use(express.cookieParser()); app.use(express.session({secret: "secret", store: new RedisStore(), cookie: {maxAge: 60 * 60 * 1000}})); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); // Routes app.get('/', routes.index); app.get('/signup', routes.signup); app.post('/signup', routes.create_signup); app.get('/login', routes.login); app.post('/login', routes.create_login); app.get('/logout', routes.logout); app.get('/count/:id', routes.count); app.listen(3000, function(){ console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); });
Routesあたりがぐちゃぐちゃだけど、仕方ないのかな?まあまだ手探り。
とりあえずroutes/index.jsはこんな感じ
exports.index = function(req, res){ res.render('index', { title: "Welcome My Chat by " + req.session.name }); }; exports.signup= function(req, res){ res.render('signup', { title: 'signup' }); }; exports.create_signup= function(req, res){ if (! /[0-9a-zA-Z\-_]+/.test(req.body.name)){ res.send('name は 半角英数で'); res.redirect('/signup'); } if (req.body.name.length < 8){ res.send('name は 8文字以上にしましょう'); res.redirect('/signup'); } if (! /[0-9a-zA-Z\-_]+/.test(req.body.password)){ res.send('password は 半角英数で'); res.redirect('/signup'); } if (req.body.password.length < 8){ res.send('password は 8文字以上にしましょう'); res.redirect('/signup'); } if (req.body.password != req.body.password2) { res.send('パスワード確認が間違ってるよ'); res.redirect('/signup'); } req.session.name = req.body.name; req.session.password = req.body.password; req.session.password2 = req.body.password2; res.redirect('/'); }; exports.login= function(req, res){ res.render('login', { title: 'login' }); }; exports.create_login= function(req, res){ //res.render('login', { title: 'login' }); if (req.session.password != req.session.password2) res.sender('differendt password'); }; exports.logout= function(req, res){ //res.render('logout', { title: 'logout' }); delete req.session.name; delete req.session.password; delete req.session.password2; req.session.name = "My Name"; res.redirect('/'); }; exports.count= function(req, res){ //res.render('count', { title: 'count' }); res.send('user' + req.params.id); };
簡単なバリデーションとか :id でパラメータとってくるテストとかしてた。あとで消す予定。よくわからんがこれでsessionは動いてくれた。
動作について簡単になにか
input 要素でとってきたPOSTには req.body.(name) でアクセスできる。というか req.body がObjectである。で、それに対して req.session というオブジェクトに対して req.session.(name) みたいにキーを突っ込んでやる、と。
次の課題
- RedisでDBとしてユーザーを保存したいんだけどどうやるん?
- Redisそのものをつかうのかな?ハッシュ?
- node_redisがそのまんなRedisつかえるから使い方を学べばできそうだが、さて……
- redis.incrがboolean返すからなんか連番できないんですけど?
- JSONにするよりはKVSとしてなんか他のGood Practiceでやるほうがいいらしい
- RedisでDBにチャット履歴を保存したいんだけどどうやるん?
- Redisのリスト?
- Socket.idとExpressのセッション共有どうしよう?
- 先例があるから調べてどうこうするけどハマりそう
- そもそもチャット的な画面とかどうしよう?
課題はあれど、とりあえずセッションが扱えるならログイン周りはできそうかなー。もうちょいかなー
参考URL
node.jsのexpressでreq.sessionがundefinedになる場合にはapp.configureのセクション内容の書き順を疑うこと - memo.yomukaku.net
node.js + expressでTwitter認証 | FIRN.JP
visionmedia/connect-redis · GitHub
node.js + express でセッションストアを Redis に変更する方法 | FIRN.JP
chromeデベロッパーツールでjavascriptのデバッグをする -node.jsもあるでよ- - 馬鹿と天才は紙一重
node-inspectorとnode-devでnode.jsをデバックする方法 - DRY(日本やアメリカで働くエンジニア日記)