2013年6月16日日曜日

[Play] Play Framework のセッション

Play Framework には Tomcat のような HTTP セッションがない。
Play でのセッションは Cookie として実現されている。
Play でセッションを使う場合にはこのようなコードになる。
package controllers

import play.api._
import play.api.mvc._
import play.api.mvc.RequestHeader

object Application extends Controller {
  def index = Action.apply { request => 
      val currentVal : String = request.session.get("hoge") match {
         case Some(x) => x;
         case None => "";
      }
      
      Ok("hoge = " + currentVal).withSession(
            request.session + ("hoge" -> "hogege"));
  }
}

request.session.get は Option を返すので、 パターンマッチで SomeNone かを判定して、Some から値を取り出す必要がある。
もしくは、Option.getOrElse を使って、
      ...
      val currentVal : String = request.session.get("hoge").getOrElse("");
      ...
とする。こっちの方が簡単でいい。



レスポンスヘッダ


このApplication へのアクセスの Response ヘッダは以下のようになっている。

Content-Length:7
Content-Type:text/plain; charset=utf-8
Set-Cookie:PLAY_SESSION=086cb3ef28d787e60a6a1dfd4f880c201de6521e-hoge%3Ahogege;Path=/;HTTPOnly

PLAY_SESSION という Cookie に Application でセッションにセットした値がそのまま入っている。
したがって、セッションに保持できるのは 文字列にした情報のみで あまり長い値を保持することはできない (RFC-2965 によると 少なくとも1Cookie あたり 4096 バイトまで実装せよ、とのことなのでそれ以上は保証されない)。
また、サーバー側でセットした値がそのまま平文で繰り返し流されることになるので秘密情報を載せることもできない。


PLAY_SESSION クッキーは編集不可


PLAY_SESSION クッキーの前半部分の文字列は署名になっているらしく、 PLAY_SESSION クッキーの値をクライアント側で勝手に編集すると、 Play側ではPLAY_SESSION クッキーはなかったものと扱われる。

実際に Chrome の Edit This Cookie Plugin 等を使って値を編集すると Play 側で値を取得できない。
Request Header
....
Cookie:PLAY_SESSION=086cb3ef28d787e60a6a1dfd4f880c201de6521e-hoge%3Ahogaga
Host:localhost:9000
....
Applicationの表示
hoge = 

クライアント側で PLAY_SESSION クッキーの hoge 値を編集したものが リクエストヘッダに乗っかっているが、Application では この値は取得されない。


PLAY_SESSION を HTTPSession のように使いたいのなら Cache を使う。


HTTPSession のように Cookie に保持できない値は サーバー側で保持するなら play.api.Cache を使う。
package controllers

import play.api._
import play.api.cache._
import play.api.mvc._
import play.api.mvc.RequestHeader

object Application extends Controller {
  def index = Action.apply { request => 

      var sessionId : String = request.session.get("id") match {
         case Some(x) => x;
         case None => java.util.UUID.randomUUID().toString();
      }

      import play.api.Play.current;
      var value : String = Cache.get(sessionId) match {
         case Some(x) => Cache.set(sessionId, x, 10); x.asInstanceOf[String];
         case None => Cache.set(sessionId, "hoge", 10); "";
      }
      Cache.set(sessionId + "hoge", "hogege", 10);

      Ok("value = " + value).withSession(
            request.session + ("id" -> sessionId));
  }
}

Cache を使う際には、「import play.api.Play.current」を入れておかないとエラーになる。
You do not have an implicit Application in scope. 
If you want to bring the current running Application into context, 
just add import play.api.Play.current

Play の セッションには位置な識別子がないので、UUIDなどを自分で生成して Session に格納しておく。
Cache にセットする際には Expire(秒)が指定できるが、「取得しただけでは更新されない」ので、 毎回、新たにセットしなおして更新しないと、最初に入れてから Expire 時間が過ぎると 期限切れでなくなってしまう。

0 件のコメント:

コメントを投稿