Ratpack入門シリーズ
- Ratpack入門 (1) - Ratpackとは
- Ratpack入門 (2) - アーキテクチャー
- Ratpack入門 (3) - hello world 詳解
- Ratpack入門 (4) - ルーティング & 静的コンテンツ
- Ratpack入門 (5) - Json & Registry
- Ratpack入門 (6) - Promise
- Ratpack入門 (7) - Guice & Spring
- Ratpack入門 (8) - セッション
- Ratpack入門 (9) - Thymeleaf
セッション
Ratpackはデフォルトではセッションを提供しません。いわゆるStickyセッションを使うには、ratpack-session
モジュールが必要です。Redisによるセッションサポートを有効にするには、さらにratpack-session-redis
モジュールが必要です。
dependencies {
compile "io.ratpack:ratpack-session:1.5.1"
}
モジュールを登録する必要があります。前述のようにRatpackは、拡張モジュールをGuiceのモジュールとして提供しています。
Function<Registry, Registry> registry = ratpack.guice.Guice.registry( bindings -> {
bindings.module( SessionModule.class );
} );
セッションを管理するSession
クラスをRegistry
から取得して、セッションのデータを操作します。ハンドラーのContext
はRegistry
を継承しているので、Context.get()
メソッドを呼び出して、セッションを取得できます。
Session session = context.get( Session.class );
セッションのキーには、Stringやクラス自体を使用することができます。しかしStringのキーではデータの取得にキャストが必要になり、クラスを使用する場合は同じクラスを登録できなくなります。一番いい方法は、SessionKey<T>
クラスを使用することです。
public static final SessionKey<String> KEY = SessionKey.of( "KEY_NAME", String.class );
キーの名前と型を指定することで、安全にデータのやり取りを行うことができます。
データの取得及び設定には、それぞれget(SessionKey)
およびset(SessionKey<T>, T)
を使用します。これらの操作はブロッキング操作として扱われます。従って、これらのメソッドは直接値を返すのではなく、Promise
およびOperation
を返します。これはセッションの情報がどう保存されるかは実装依存であり、サーバー内部ではなく外部のKVSなどに保存されることも想定しているためです。
以下、ユーザーがサイトを訪れた回数と時間を保存する例です。
Session session = ctx.get( Session.class );
session.get( countKey ).flatRight( v -> session.get( lastVisit ) ).then( pair -> {
int count = pair.left.orElse( 0 );
String response = count == 0
? "This is the first visit."
: "You have visited " + count + " times.\n" +
"Last visit: " + pair.right.get().format( DateTimeFormatter.ISO_LOCAL_DATE_TIME );
session.set( countKey, count + 1 )
.next( session.set( lastVisit, LocalDateTime.now() ) )
.then( () -> {
ctx.render( response );
} );
} );
get()
が返す型はPromise<Optional<T>>
です。Optional
にラップしてくれるので、Servletのようにnullチェックする必要がありません。
見てのとおり、複数の値を取得しようとするとPromise
が重なってコードが汚くなります。getData()
メソッドで、まとめて値を取得できます。
また、保存されたデータはJavaのシリアライズ機能によって直列化されます。そのため、Serializable
を実装している必要があります。シリアライズはSessionSerializer
によって行われます。Registry
に独自の実装を提供することで、シリアライズの手法を変更できます。
ClientSideSessionModule
ratpack-sessionのデフォルト実装はGuavaのキャッシュを利用したインメモリーキャッシュを使います。一方、ClientSideSessionModule
を使用することで、クッキーに暗号化してデータを保存する、クライアントサイドセッションを使うモジュールも用意されています。
デフォルトでは情報は暗号化されません。必ずキーを指定する必要があります。
ratpack-session-redis
セッション情報をRedisに保存するために、ratpack-session-redisモジュールが用意されています。
dependencies {
compile "io.ratpack:ratpack-session-redis:1.5.1"
}
モジュールを設定します。
RedisSessionModule redisModule = new RedisSessionModule();
redisModule.configure( config -> {
config.setHost( "localhost" );
config.setPort( 6379 );
config.setPassword( null );
} );
Function<Registry, Registry> registry = ratpack.guice.Guice.registry( bindings -> {
bindings.module( SessionModule.class );
bindings.module( redisModule );
} );
RedisSessionModule
は、必ずSessionModule
の後に登録する必要があります。Ratpackは後から登録されたクラスを優先します。順番を逆にしてしまうと、Session
の実装がRedisではなく、Stickyセッションのものになってしまいます。
ここではホストやポートをプログラムから設定していますが、ConfigSource
から設定することができます。
使い方自体は通常のratpack-sessionと同様です。設定してしまえば、バックエンドがRedisになっていることを意識することはありません。
Redisサーバーには、セッションIDをそのままキーとして、データが保存されます。セッションIDはデフォルトではUUIDが使われます。IDの生成はSessionIdGenerator
経由で行われ、Registry
から実装を切り替えられます。
# redis-cli
127.0.0.1:6379> keys *
1) "036f9723-3df8-e018-9abe-2db2bca5f6f6"
127.0.0.1:6379> get 036f9723-3df8-e018-9abe-2db2bca5f6f6
"\xac\xed\x00\x05sr\x006ratpack.session.internal.DefaultSession$SerializedForm\x00\x00\x00\x00\x00\x00\x00\x02\x0c\x00\x00xpw\xd3\x00\x01\x00\x02\x01\x00\x05COUNT\x01\x00\x11java.lang.Integer\x00\x00\x00
Q\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x02\x01\x00\nLAST_V
ISIT\x01\x00\x17java.time.LocalDateTime\x00\x00\x003\xac\xed\x00\x05sr\x00\rjava.time.Ser\x95]\x84\xba\x1b\"H\xb2\x0c\x00\x00xpw\x0e\x05\x00\x00\a\xe2\x02\t\r8!\x1a\xe1\xb6\xc0xx"
127.0.0.1:6379>
ちなみに、Redisとの通信はLettuceを使用しています(いつもキャベツと間違える)。