NodeでRedisでExpressでSessionをはってやるまで

開発環境を整える

まともなデバッグ環境がないと厳しい

node-dev

ファイルの更新があるたびにサーバーを再起動してくれる

npm install -g node-dev
node-inspector

Chrome上でデバッグできるようにする

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のセッション共有どうしよう?
    • 先例があるから調べてどうこうするけどハマりそう
  • そもそもチャット的な画面とかどうしよう?

課題はあれど、とりあえずセッションが扱えるならログイン周りはできそうかなー。もうちょいかなー