2010年3月5日金曜日

[Lift] LiftFormを使わずにStatefulSnippetをステートフルに使う

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
LiftFormは大変便利なのですが、Lift流にsubmit時のコールバックをクロージャで書いてしまうと、セッションが切れた時にコールバックが動きません。Google App Engine上で動かしたりなんかすると、3分程度でスピンダウンしてしまうので、すぐ切れてしまってフォーム入力に時間がかかるようなアプリだと、セッション切れてまともに動きません…。(※実際は、ajax_requestが定期的にコールされるので、そんなにスピンダウンしない?と思うけど今作ってるのが携帯向けサイトでajax_requestをOFFにしてるので問題が顕在化)

そこで、クラシックな通常のHTMLフォームを使い、なおかつStatefulSnippetとして同じインスタンスが呼ばれるようなコードを書いてみました。

ソースを見る

まずはLift流に

普通にLift流に簡単なフォームを書くとこんな感じです。
<lift:StatefulTest.liftForm form="POST">
  <e:instance/>
  <e:input/>
  <e:submit/>
</lift:StatefulTest.liftForm>

Snippetはこんな感じ。
def liftForm(in: NodeSeq): NodeSeq = {
var name = ""
def sayHello() = {
  S.notice("Hello, " + name + ". I'm " + this)
  redirectTo("/liftSayHello")
}
bind("e", in,
  "instance" -> Text("I'm " + this),
  "input" -> SHtml.text(name, i => name = i),
  "submit" -> SHtml.submit("say hello", sayHello)
)
}

無理やり普通のformで

これと同様なフォームを、無理やり書くとこんな感じです。
<lift:StatefulTest.myForm>
  
  <form action="mySayHello" method="POST"> 
    <e:instance/>
    <e:key/>
    <e:input/>
    <e:submit/>
  </form>
</lift:StatefulTest.myForm>

ポイントは、registerThisSnippet()を呼ぶ関数ブロックを、S.fmapFuncで自分で関数マップに登録してやり、そのキーをhiddenで埋め込んでやること(*1)。
def myForm(in: NodeSeq): NodeSeq = {
S.fmapFunc((a: List[String]) => {registerThisSnippet()})(key => {
  bind("e", in,
    "key" -> <input type="hidden" name={key} value="_"/>, // *1
    "instance" -> Text("I'm " + this),
    "input" -> <input type="text" name="name"/>,
    "submit" -> <input type="submit" value="say hello"/>
  )
})
}
このやり方だと、セッションが続いていれば同じインスタンスのStatefulSnippetが呼ばれ、続いていない場合でも、少なくとも新しいインスタンスのStatefulSnippetで処理は行えます。(CSRFの問題がありますが…)

ソースはgithubに置きました。http://github.com/pomu0325/lift_sandbox

0 件のコメント:

コメントを投稿