CompojureでSessionに何かデータ保存したいと思ったらまだCompojureでSessionを使ったことがなかったので簡単に使ってみました。
Compojure(Ring)でセッションを使う場合、handlerにwrap-sessionを指定してあげるとCookieセッションが使える。デフォルトだとデータの保存先はメモリ。
で、少しSession関連のモジュールを調べてみたところ、sandbarというのがwrap-sessionのラッパーで高機能かつ、インタフェースが使いやすかったので、今回はコレを使うことにしました。
Session Wrapper
sandbarはwrap-sessionのラッパーというだけでなく、セッションを使ったサイトでよく使われるような、ログインユーザの名前や権限を取得する関数等も提供されています。
brentonashworth/sandbar – GitHub
実際に組み込み後のコードはこんな感じになりました。
(ns compojuretest.core (:use compojure.core compojure.response ring.adapter.jetty ring.middleware.reload-modified somnium.congomongo [ring.util.response :only (response content-type)] fleet + (sandbar stateful-session auth)) ... 省略 ... ;; ============================================= ;; Routes ;; ============================================= (defroutes app-routes (GET "/" {} (tpl/index {}))) +(def app + (-> app-routes + wrap-stateful-session)) ;; ============================================= ;; Run Server ;; ============================================= ;; AutoReload Configuration (def app (handler/site app)) (def handlerApp (wrap-reload-modified #'app ["src"])) (defn -main [] (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))] (run-jetty handlerApp {:port port}))) |
これで、Sessionが使えるようになります。Sessionを取り出したり設定したりするAPIは本家のwikiを見るか、行数が少ないのでソースを直接見たほうが早いかもしれません。該当箇所は以下です。
src/sandbar/stateful_session.clj at master from brentonashworth/sandbar – GitHub
保存先
上記に書いた通り、標準のSessionの保存先はメモリとなっているので、サーバの再起動等で消えてしまいます。少し不安でもあったので、ファイルかDBに保存するモジュールが無いか調べました。
その前にwrap-sessionで指定できるSessionの保存先というのはオプションで指定でき、下記の継承したものであれば良いようです。
ring-core/src/ring/middleware/session/store.clj at master from mmcgrana/ring – GitHub
SessionもMongoDBに保存したい!
せっかく今回、MongoDBを使っているので、セッションもMongoDBに格納したい!!
clojars.orgで検索してみたところ、下記のモジュールが見つかりました。
hozumi/mongodb-session – GitHub
サンプルを見たところ
(ns hello (:require [ring.middleware.session :as rs] [somnium.congomongo :as congo] [hozumi.mongodb-session :as mongoss] ...)) (congo/mongo! :db "mydb" :host "127.0.0.1") (defroutes my-routes ....) (def app (-> my-routes (rs/wrap-session {:store (mongoss/mongodb-store)}))) |
とあるので、既に宣言しているmongo!の周り無視して、wrap-session辺りを修正するだけで大丈夫なようです。mongodb-storeを組み込んだコードはこんな感じになりました。
(ns compojuretest.core (:use compojure.core compojure.response ring.adapter.jetty ring.middleware.reload-modified somnium.congomongo [ring.util.response :only (response content-type)] fleet (sandbar stateful-session auth)) (:require [compojure.route :as route] [compojure.handler :as handl(mongoss/er] [clj-json.core :as json] [clojure.contrib.string :as string] + [hozumi.mongodb-session :as mongoss])) ... 省略 ... ;; ============================================= ;; Routes ;; ============================================= (defroutes app-routes (GET "/" {} (tpl/index {}))) (def app (-> app-routes - wrap-stateful-session + (wrap-stateful-session {:store (mongoss/mongodb-store)}))) ;; ============================================= ;; Run Server ;; ============================================= ;; AutoReload Configuration (def app (handler/site app)) (def handlerApp (wrap-reload-modified #'app ["src"])) (defn -main [] (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))] (run-jetty handlerApp {:port port}))) |
この状態で実際にセッションに値を格納するとMongoDBの方にring_sessionsというコレクションが出来ていると思います。ちなみに、ring_sessionsのレコードはこんな形式です。
> db.ring_sessions.find().forEach(printjson); { "_id" : "c83528b0-cfce-4e0f-b70b-90adce0c21d5", "_date" : ISODate("2011-10-31T17:34:55.022Z"), "_sandbar_session" : { "name" : "Tatsuya Fukata" } }
Cookieのオプション
wrap-sessionで生成されるCookieはデフォルトではexpires未指定でブラウザを閉じた場合に消去されるものになっています。これらのオプションを指定する場合には、wrap-sessionの引数として下記のように指定します。また、これはwrap-stateful-sessionでも同じ形式の引数で渡すことが出来ます。
(def app (-> app-routes (wrap-stateful-session {:cookie-attrs {:expires "Thu, 1-Jan-2030 00:00:00 GMT", :path "/"}, :cookie-name "compojuretest-session", :store (mongoss/mongodb-store)}))) |
上記以外の詳しい指定方法については、下記のソースを参照してみてください。
ring-core/src/ring/middleware/session.clj at master from mmcgrana/ring – GitHub