Przejdź do treści

Redis & Sessions

Redis can act as a storage provider for caching session data such as user credentials.

If a custom RedisSessionsDelegate isn't provided, a default will be used.

Default Behavior

SessionID Creation

Unless you implement the makeNewID() method in your own RedisSessionsDelegate, all SessionID values will be created by doing the following:

  1. Generate 32 bytes of random characters
  2. base64 encode the value

For example: Hbxozx8rTj+XXGWAzOhh1npZFXaGLpTWpWCaXuo44xQ=

SessionData Storage

The default implementation of RedisSessionsDelegate will store SessionData as a simple JSON string value using Codable.

Unless you implement the makeRedisKey(for:) method in your own RedisSessionsDelegate, SessionData will be stored in Redis with a key that prefixes the SessionID with vrs- (Vapor Redis Sessions)

For example: vrs-Hbxozx8rTj+XXGWAzOhh1npZFXaGLpTWpWCaXuo44xQ=

Registering A Custom Delegate

To customize how the data is read from and written to Redis, register your own RedisSessionsDelegate object as follows:

import Redis

struct CustomRedisSessionsDelegate: RedisSessionsDelegate {
    // implementation
}

app.sessions.use(.redis(delegate: CustomRedisSessionsDelegate()))

RedisSessionsDelegate

API Documentation: RedisSessionsDelegate

An object that conforms to this protocol can be used to change how SessionData is stored in Redis.

Only two methods are required to be implemented by a type conforming to the protocol: redis(_:store:with:) and redis(_:fetchDataFor:).

Both are required, as the way you customize writing the session data to Redis is intrinsically linked to how it is to be read from Redis.

RedisSessionsDelegate Hash Example

For example, if you wanted to store the session data as a Hash in Redis, you would implement something like the following:

func redis<Client: RedisClient>(
    _ client: Client,
    store data: SessionData,
    with key: RedisKey
) -> EventLoopFuture<Void> {
    // stores each data field as a separate hash field
    return client.hmset(data.snapshot, in: key)
}
func redis<Client: RedisClient>(
    _ client: Client,
    fetchDataFor key: RedisKey
) -> EventLoopFuture<SessionData?> {
    return client
        .hgetall(from: key)
        .map { hash in
            // hash is [String: RESPValue] so we need to try and unwrap the
            // value as a string and store each value in the data container
            return hash.reduce(into: SessionData()) { result, next in
                guard let value = next.value.string else { return }
                result[next.key] = value
            }
        }
}