学習がてら、HaskellのWebフレームワークであるYesodと、セッション管理をサポートするライブラリであるserversessionを組み合わせて動かしてみました。
今回は、stackのtemplateであるyesodweb/simpleを利用して、redissessionというプロジェクト名にしました。
stack new redissession yesodweb/simple
今回は、SessionストレージにRedisを使いたかったので、serversessionというライブラリを使いました。
(このライブラリは長いことメンテナンスされていないようですが、今回はHaskellでのセッション周りの実装例を見てみることが目的なので、気にせず進みます。)
まずは、package.yamlにserversessionパッケージの情報を追記します。
フロントエンドとしてYesod、バックエンドとしてRedisを使用するので、serversession-frontend-yesodとserversession-backend-redisも追記します。
また、serversesion-backend-redisはhedisというパッケージに依存しているので、それも追記します。
dependencies:
(中略)
- serversession == 1.0.1
- serversession-frontend-yesod == 1.0
- serversession-backend-redis == 1.0.3
- hedis < 0.11
serversession-frontend-yesodとserversession-backend-redisは、今回使用したStackageのresolverのlts-16.20に含まれていなかったため、stack.yamlにも追記します。
また、このままではパッケージ間のバージョン依存関係の不整合によりエラーが発生してしまったので、"allow-newer: true"を設定して、バージョン依存関係の不整合を無視します。
今回はまず動かしてみることが目的なので、これでよしとします。
extra-deps:
- serversession-frontend-yesod-1.0
- serversession-backend-redis-1.0.3
allow-newer: true
import ...(略)
-- 以下を追記
import Web.ServerSession.Backend.Redis (RedisStorage(..))
import Web.ServerSession.Frontend.Yesod (simpleBackend, setCookieName, setSecureCookies)
import qualified Database.Redis as Redis
(中略)
data App = App
{ appSettings :: AppSettings
, appStatic :: Static -- ^ Settings for static file serving.
, appHttpManager :: Manager
, appLogger :: Logger
, appConn :: Redis.Connection --追加
}
instance Yesod App where
(中略)
makeSessionBackend :: App -> IO (Maybe SessionBackend)
makeSessionBackend = simpleBackend opts . createStorage
where createStorage appl = RedisStorage (appConn appl) idleTimeoutSec absoluteTimeoutSec
idleTimeoutSec = Just $ 60*10
absoluteTimeoutSec = Just $ 60*30
opts = setCookieName "REDISSESSION_ID"
. setSecureCookies False
import qualified Database.Redis as Redis
makeFoundation :: AppSettings -> IO App
makeFoundation appSettings = do
-- Some basic initializations: HTTP connection manager, logger, and static
-- subsite.
appHttpManager <- getGlobalManager
appLogger <- newStdoutLoggerSet defaultBufSize >>= makeYesodLogger
appStatic <-
(if appMutableStatic appSettings then staticDevel else static)
(appStaticDir appSettings)
appConn <- Redis.connect Redis.defaultConnectInfo -- ここを追記
-- Return the foundation
return App {..}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE QuasiQuotes #-} --追加
import Web.ServerSession.Frontend.Yesod (forceInvalidate, ForceInvalidate(..))
import Import
import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3)
import Text.Julius (RawJS (..))
import Web.ServerSession.Frontend.Yesod (forceInvalidate, ForceInvalidate(..))
getHomeR :: Handler Html
getHomeR = do
sess <- getSession
forceInvalidate CurrentSessionId -- これを呼ぶとセッションIDが再採番される
defaultLayout
[whamlet|
<form method=post>
<input type=text name=key>
<input type=text name=val>
<input type=submit>
<h1>#{show sess}
|]
postHomeR :: Handler ()
postHomeR = do
(key, mval) <- runInputPost $ (,) <$> ireq textField "key" <*> iopt textField "val"
case mval of
Nothing -> deleteSession key
Just val -> setSession key val
liftIO $ print (key, mval)
redirect HomeR
動かしてみます。
Redisを立ち上げておきます。
$ redis-server
Webサーバを起動します
$ stack exec -- yesod devel
Webサーバが立ち上がったので、Webブラウザで、localhost:3000にアクセスします。
Set-Cookieヘッダーに、「REDISSESSION_ID」フィールドがあります。うまく動いていそうです。
次に、Sessionストレージにデータを保存してみます。
Keyは「hoge」、Valueは「fuga」を指定します。
Session情報が画面に表示されました。ちゃんと動いていそうです。
(「forceInvalidate CurrentSessionId」を呼んだので、SetCookieヘッダーの「REDISSESSION_ID」の値が変わっていますが、このタイミングでは本来必要ありません。)
redis-cliでも、Redisに格納されているデータを確認しておきます。
ブラウザで入力したデータが、ちゃんと保存されています。