wasabifx / wasabi

An HTTP Framework
501 stars 56 forks source link

Type-safe session-data #98

Open stangls opened 7 years ago

stangls commented 7 years ago

Currently, session-data is stored as type "Any?". It would be great to have session-data stored in a type-safe way.

At the moment, I have implemented these extension functions to satisfy my requirement:

/**
 * Enables session support for storing arbitrarily typed data.
 * @param initGenerator specifies how session-data is initialized
 * @return An object to retrieve the data in a type-safe way.
 */
fun <T> AppServer.enableTypesafeSessionSupport( initGenerator: (Request) -> T ) : SessionFetcher<T> {
    enableSessionSupport()
    return SessionFetcher<T>(initGenerator)
}
/**
 * Enables session support for storing arbitrarily typed data.
 * @return An object to retrieve the data in a type-safe way.
 */
fun <T> AppServer.enableTypesafeSessionSupport() : SessionFetcher<T?> {
    return enableTypesafeSessionSupport({null})
}

class SessionFetcher<T> internal constructor(val initGenerator: (Request) -> T ) {

    protected val values = ConcurrentHashMap<String,T>()

    operator fun get(request:Request) : T {
        // session must to be there, because session-fetcher is internally created only
        val key = request.session!!.id
        return values.getOrPut( key, { initGenerator(request) } )
    }

}

This can then for example be used like this:

data class SessionData (
    var userId : Int? = null
    // here you may put further stuff to be type-safely stored,
    // for example persisted data of a database or even write functions for retrieving them
)

// easy-access extension-function
val Request.tSession: SessionData
    get() = MyApp.sessionDataFetcher[this]

// application example
object MyApp {
    val sessionDataFetcher: SessionFetcher<SessionData>
    private val server : AppServer
    init {
        server = AppServer()
        sessionDataFetcher = server.enableTypesafeSessionSupport { SessionData() }
        server.get("/", {
            response.send("Your user id is ${request.tSession.userId.toString()}")
        })
        server.get("/set", {
            request.tSession.userId = Random().nextInt()
            response.send("Your user id has been randomly set.")
        })
        server.addStyleSheet(MainCss(),true)
    }
    @JvmStatic fun main(args: Array<String>) {
        server.start()
    }
}
stangls commented 7 years ago

It would be great to have this or some other kind of type-safety for session-data introduced into the SessionManagementInterceptor, because when you use the code this way, we create multiple concurrent hash maps.