Repository: tipsy/javalin
Pull request #1311 (2021-07-05 21:22:55)

[context] Bring back reified methods with "asClass" suffix


Patch:
diff --git a/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLHandler.kt b/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLHandler.kt
index f510e724..44a73208 100644
--- a/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLHandler.kt
+++ b/javalin-graphql/src/main/java/io/javalin/plugin/graphql/GraphQLHandler.kt
@@ -32,7 +32,7 @@ class GraphQLHandler(val options: GraphQLOptions) {
     }
 
     fun execute(ctx: WsMessageContext) {
-        val body = ctx.message(Map::class.java)
+        val body = ctx.messageAsClass(Map::class.java)
         this.options.wsMiddleHandler(ctx)
         this.genericExecute(body)
             .subscribe(SubscriberGraphQL(ctx))
diff --git a/javalin/src/main/java/io/javalin/http/Context.kt b/javalin/src/main/java/io/javalin/http/Context.kt
index 3b0c3397..f76c5096 100644
--- a/javalin/src/main/java/io/javalin/http/Context.kt
+++ b/javalin/src/main/java/io/javalin/http/Context.kt
@@ -110,21 +110,22 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
      */
     fun bodyAsBytes(): ByteArray = body
 
-    /**
-     * Maps a JSON body to a Java/Kotlin class using the registered [JsonMapper].
-     * @return The mapped object
-     */
+    /** Maps a JSON body to a Java/Kotlin class using the registered [JsonMapper] */
     fun <T> bodyAsClass(clazz: Class<T>): T =
         jsonMapper().let { if (it.canReadStream()) it.fromJsonStream(req.inputStream, clazz)!! else it.fromJsonString(body(), clazz)!! }
 
-    /**
-     * Gets the request body as a [InputStream]
-     */
+    /** Reified version of [bodyAsClass] (Kotlin only) */
+    inline fun <reified T : Any> bodyAsClass(): T = bodyAsClass(T::class.java)
+
+    /** Gets the request body as a [InputStream] */
     fun bodyAsInputStream(): InputStream = req.inputStream
 
-    /** Creates a [BodyValidator] for the body() value */
+    /** Creates a typed [BodyValidator] for the body() value */
     fun <T> bodyValidator(clazz: Class<T>) = BodyValidator(body(), clazz, this.jsonMapper())
 
+    /** Reified version of [bodyValidator] (Kotlin only) */
+    inline fun <reified T : Any> bodyValidator() = bodyValidator(T::class.java)
+
     /** Gets first [UploadedFile] for the specified name, or null. */
     fun uploadedFile(fileName: String): UploadedFile? = uploadedFiles(fileName).firstOrNull()
 
@@ -141,11 +142,11 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
     /** Gets a form param if it exists, else null */
     fun formParam(key: String): String? = formParams(key).firstOrNull()
 
-    /**
-     * Creates a [Validator] for the formParam() value, with the prefix "Form parameter '$key' with value '$value'"
-     * Throws [BadRequestResponse] if validation fails.
-     */
-    fun <T> formParam(key: String, clazz: Class<T>) = Validator.create(clazz, formParam(key), key)
+    /** Creates a typed [Validator] for the formParam() value */
+    fun <T> formParamAsClass(key: String, clazz: Class<T>) = Validator.create(clazz, formParam(key), key)
+
+    /** Reified version of [formParamAsClass] (Kotlin only) */
+    inline fun <reified T : Any> formParamAsClass(key: String) = formParamAsClass(key, T::class.java)
 
     /** Gets a list of form params for the specified key, or empty list. */
     fun formParams(key: String): List<String> = formParamMap()[key] ?: emptyList()
@@ -164,13 +165,13 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
      */
     fun pathParam(key: String): String = ContextUtil.pathParamOrThrow(pathParamMap, key, matchedPath)
 
-    /**
-     * Creates a [Validator] for the pathParam() value, with the prefix "Path parameter '$key' with value '$value'"
-     * Throws [BadRequestResponse] if validation fails.
-     */
-    fun <T> pathParam(key: String, clazz: Class<T>) = Validator.create(clazz, pathParam(key), key)
+    /** Creates a typed [Validator] for the pathParam() value */
+    fun <T> pathParamAsClass(key: String, clazz: Class<T>) = Validator.create(clazz, pathParam(key), key)
+
+    /** Reified version of [pathParamAsClass] (Kotlin only) */
+    inline fun <reified T : Any> pathParamAsClass(key: String) = pathParamAsClass(key, T::class.java)
 
-    /** Gets a map of all the [pathParam] keys and values. */
+    /** Gets a map of all the [pathParamAsClass] keys and values. */
     fun pathParamMap(): Map<String, String> = Collections.unmodifiableMap(pathParamMap)
 
     /**
@@ -213,8 +214,11 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
     /** Gets a request header by name, or null. */
     fun header(header: String): String? = req.getHeader(header)
 
-    /** Creates a [Validator] for the header() value, with the prefix "Request header '$header' with the value '$value'" */
-    fun <T> header(header: String, clazz: Class<T>): Validator<T> = Validator.create(clazz, header(header), header)
+    /** Creates a typed [Validator] for the header() value */
+    fun <T> headerAsClass(header: String, clazz: Class<T>): Validator<T> = Validator.create(clazz, header(header), header)
+
+    /** Reified version of [headerAsClass] (Kotlin only) */
+    inline fun <reified T : Any> headerAsClass(header: String) = headerAsClass(header, T::class.java)
 
     /** Gets a map with all the header keys and values on the request. */
     fun headerMap(): Map<String, String> = req.headerNames.asSequence().associate { it to header(it)!! }
@@ -246,11 +250,11 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
     /** Gets a query param if it exists, else null */
     fun queryParam(key: String): String? = queryParams(key).firstOrNull()
 
-    /**
-     * Creates a [Validator] for the queryParam() value, with the prefix "Query parameter '$key' with value '$value'"
-     * Throws [BadRequestResponse] if validation fails.
-     */
-    fun <T> queryParam(key: String, clazz: Class<T>) = Validator.create(clazz, queryParam(key), key)
+    /** Creates a typed [Validator] for the queryParam() value */
+    fun <T> queryParamAsClass(key: String, clazz: Class<T>) = Validator.create(clazz, queryParam(key), key)
+
+    /** Reified version of [queryParamAsClass] (Kotlin only) */
+    inline fun <reified T : Any> queryParamAsClass(key: String) = queryParamAsClass(key, T::class.java)
 
     /** Gets a list of query params for the specified key, or empty list. */
     fun queryParams(key: String): List<String> = queryParamMap()[key] ?: emptyList()
diff --git a/javalin/src/main/java/io/javalin/http/context/ContextExt.kt b/javalin/src/main/java/io/javalin/http/context/ContextExt.kt
deleted file mode 100644
index 8410f196..00000000
--- a/javalin/src/main/java/io/javalin/http/context/ContextExt.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package io.javalin.http.context
-
-import io.javalin.http.Context
-
-/**
- * Maps a JSON body to a Java/Kotlin class using JavalinJson.
- * JavalinJson can be configured to use any mapping library.
- * @return The mapped object
- */
-inline fun <reified T : Any> Context.body(): T = bodyAsClass(T::class.java)
-
-/** Reified version of [header] (Kotlin only) */
-inline fun <reified T : Any> Context.header(header: String) = header(header, T::class.java)
-
-/** Reified version of [formParam] (Kotlin only) */
-inline fun <reified T : Any> Context.formParam(key: String) = formParam(key, T::class.java)
-
-/** Reified version of [pathParam] (Kotlin only) */
-inline fun <reified T : Any> Context.pathParam(key: String) = pathParam(key, T::class.java)
-
-/** Reified version of [queryParam] (Kotlin only) */
-inline fun <reified T : Any> Context.queryParam(key: String) = queryParam(key, T::class.java)
-
-/** Reified version of [bodyValidator] (Kotlin only) */
-inline fun <reified T : Any> Context.bodyValidator() = bodyValidator(T::class.java)
diff --git a/javalin/src/main/java/io/javalin/websocket/WsContext.kt b/javalin/src/main/java/io/javalin/websocket/WsContext.kt
index babeaf29..a611fe01 100644
--- a/javalin/src/main/java/io/javalin/websocket/WsContext.kt
+++ b/javalin/src/main/java/io/javalin/websocket/WsContext.kt
@@ -36,11 +36,13 @@ abstract class WsContext(val sessionId: String, @JvmField val session: Session)
     fun queryParamMap(): Map<String, List<String>> = upgradeCtx.queryParamMap()
     fun queryParams(key: String): List<String> = upgradeCtx.queryParams(key)
     fun queryParam(key: String): String? = upgradeCtx.queryParam(key)
-    fun <T> queryParam(key: String, clazz: Class<T>) = upgradeCtx.queryParam(key, clazz)
+    fun <T> queryParamAsClass(key: String, clazz: Class<T>) = upgradeCtx.queryParamAsClass(key, clazz)
+    inline fun <reified T : Any> queryParamAsClass(key: String) = queryParamAsClass(key, T::class.java)
 
     fun pathParamMap(): Map<String, String> = upgradeCtx.pathParamMap()
     fun pathParam(key: String): String = upgradeCtx.pathParam(key)
-    fun <T> pathParam(key: String, clazz: Class<T>) = upgradeCtx.pathParam(key, clazz)
+    fun <T> pathParamAsClass(key: String, clazz: Class<T>) = upgradeCtx.pathParamAsClass(key, clazz)
+    inline fun <reified T : Any> pathParamAsClass(key: String) = pathParamAsClass(key, T::class.java)
 
     fun host() = upgradeReq.host // why can't we get this from upgradeCtx?
 
@@ -80,5 +82,6 @@ class WsBinaryMessageContext(sessionId: String, session: Session, private val da
 
 class WsMessageContext(sessionId: String, session: Session, private val message: String) : WsContext(sessionId, session) {
     fun message(): String = message
-    fun <T> message(clazz: Class<T>): T = upgradeCtx.jsonMapper().fromJsonString(message, clazz)
+    fun <T> messageAsClass(clazz: Class<T>): T = upgradeCtx.jsonMapper().fromJsonString(message, clazz)
+    inline fun <reified T : Any> messageAsClass(): T = messageAsClass(T::class.java)
 }
diff --git a/javalin/src/main/java/io/javalin/websocket/context/WsContext.kt b/javalin/src/main/java/io/javalin/websocket/context/WsContext.kt
deleted file mode 100644
index 61ffce35..00000000
--- a/javalin/src/main/java/io/javalin/websocket/context/WsContext.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.javalin.websocket.context
-
-import io.javalin.websocket.WsContext
-import io.javalin.websocket.WsMessageContext
-
-inline fun <reified T : Any> WsContext.queryParam(key: String) = queryParam(key, T::class.java)
-
-inline fun <reified T : Any> WsContext.pathParam(key: String) = pathParam(key, T::class.java)
-
-inline fun <reified T : Any> WsMessageContext.message(): T = message(T::class.java)


Test patch:
diff --git a/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldMultiSwagger.kt b/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldMultiSwagger.kt
index fb4f0f36..edc60bb2 100644
--- a/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldMultiSwagger.kt
+++ b/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldMultiSwagger.kt
@@ -8,8 +8,6 @@ package io.javalin.examples
 import io.javalin.Javalin
 import io.javalin.http.Context
 import io.javalin.http.InternalServerErrorResponse
-import io.javalin.http.context.body
-import io.javalin.http.context.pathParam
 import io.javalin.plugin.openapi.OpenApiOptions
 import io.javalin.plugin.openapi.OpenApiPlugin
 import io.javalin.plugin.openapi.dsl.document
@@ -43,7 +41,7 @@ val getItemDocs = document()
         .result<Unit>("404")
 
 fun getItemHandler(ctx: Context) {
-    val itemId = ctx.pathParam<Int>("id").get()
+    val itemId = ctx.pathParamAsClass<Int>("id").get()
     val item = ItemRepository.getItem(itemId)
     if (item == null) {
         ctx.status(404)
@@ -58,7 +56,7 @@ val addItemDocs = document()
         .result<Unit>("204")
 
 fun addItemHandler(ctx: Context) {
-    val item = ctx.body<Item>()
+    val item = ctx.bodyAsClass<Item>()
     ItemRepository.addItem(item)
     ctx.status(204)
 }
diff --git a/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt b/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
index 544d58ee..1a3faf53 100644
--- a/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
+++ b/javalin-openapi/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
@@ -8,8 +8,6 @@ package io.javalin.examples
 import io.javalin.Javalin
 import io.javalin.http.Context
 import io.javalin.http.InternalServerErrorResponse
-import io.javalin.http.context.body
-import io.javalin.http.context.pathParam
 import io.javalin.plugin.openapi.OpenApiOptions
 import io.javalin.plugin.openapi.OpenApiPlugin
 import io.javalin.plugin.openapi.dsl.document
@@ -47,7 +45,7 @@ val getUserDocs = document()
         .result<Unit>("404")
 
 fun getUserHandler(ctx: Context) {
-    val userId = ctx.pathParam<Int>("id").get()
+    val userId = ctx.pathParamAsClass<Int>("id").get()
     val user = UserRepository.getUser(userId)
     if (user == null) {
         ctx.status(404)
@@ -62,7 +60,7 @@ val addUserDocs = document()
         .result<Unit>("204")
 
 fun addUserHandler(ctx: Context) {
-    val user = ctx.body<User>()
+    val user = ctx.bodyAsClass<User>()
     UserRepository.addUser(user)
     ctx.status(204)
 }
diff --git a/javalin-testtools/src/test/kotlin/io/javalin/testtools/KotlinTest.kt b/javalin-testtools/src/test/kotlin/io/javalin/testtools/KotlinTest.kt
index 3c298483..7c636c07 100644
--- a/javalin-testtools/src/test/kotlin/io/javalin/testtools/KotlinTest.kt
+++ b/javalin-testtools/src/test/kotlin/io/javalin/testtools/KotlinTest.kt
@@ -2,7 +2,6 @@ package io.javalin.testtools
 
 import io.javalin.Javalin
 import io.javalin.core.util.Header
-import io.javalin.http.context.body
 import okhttp3.FormBody
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
@@ -35,7 +34,7 @@ class KotlinTest {
 
     @Test
     fun `post with json serialization works`() = TestUtil.test { server, client ->
-        server.post("/hello") { it.result(it.body<MyKotlinClass>().field1) }
+        server.post("/hello") { it.result(it.bodyAsClass<MyKotlinClass>().field1) }
         val response = client.post("/hello", MyKotlinClass("v1", "v2"))
         assertThat(response.body?.string()).isEqualTo("v1")
     }
diff --git a/javalin/src/test/java/io/javalin/TestJson.kt b/javalin/src/test/java/io/javalin/TestJson.kt
index 08b9e738..827a235d 100644
--- a/javalin/src/test/java/io/javalin/TestJson.kt
+++ b/javalin/src/test/java/io/javalin/TestJson.kt
@@ -9,8 +9,6 @@ package io.javalin
 import com.google.gson.GsonBuilder
 import io.javalin.core.util.Header
 import io.javalin.core.util.JavalinLogger
-import io.javalin.http.context.body
-import io.javalin.http.context.bodyValidator
 import io.javalin.plugin.json.JavalinJackson
 import io.javalin.plugin.json.JsonMapper
 import io.javalin.testing.NonSerializableObject
@@ -61,13 +59,13 @@ class TestJson {
 
     @Test
     fun `json-mapper maps json to object`() = TestUtil.test { app, http ->
-        app.post("/") { it.result(it.body<SerializableObject>().value1) }
+        app.post("/") { it.result(it.bodyAsClass<SerializableObject>().value1) }
         assertThat(http.post("/").body(serializableObjectString).asString().body).isEqualTo("FirstValue")
     }
 
     @Test
     fun `mapping invalid json to class throws`() = TestUtil.test { app, http ->
-        app.get("/") { it.body<NonSerializableObject>() }
+        app.get("/") { it.bodyAsClass<NonSerializableObject>() }
         assertThat(http.get("/").status).isEqualTo(500)
     }
 
@@ -135,7 +133,7 @@ class TestJson {
             override fun <T : Any?> fromJsonString(json: String, targetClass: Class<T>): T = "fromJsonString" as T
         }
         TestUtil.test(Javalin.create { it.jsonMapper(sillyMapper) }) { app, http ->
-            app.get("/") { it.result(it.body<String>()) }
+            app.get("/") { it.result(it.bodyAsClass<String>()) }
             assertThat(http.get("/").body).isEqualTo("fromJsonString")
         }
     }
@@ -147,7 +145,7 @@ class TestJson {
             override fun <T : Any?> fromJsonStream(json: InputStream, targetClass: Class<T>): T = "fromJsonStream" as T
         }
         TestUtil.test(Javalin.create { it.jsonMapper(sillyMapper) }) { app, http ->
-            app.get("/") { it.result(it.body<String>()) }
+            app.get("/") { it.result(it.bodyAsClass<String>()) }
             assertThat(http.get("/").body).isEqualTo("fromJsonStream")
         }
     }
diff --git a/javalin/src/test/java/io/javalin/TestMultipartForms.kt b/javalin/src/test/java/io/javalin/TestMultipartForms.kt
index 9accd1b7..a56d8df4 100644
--- a/javalin/src/test/java/io/javalin/TestMultipartForms.kt
+++ b/javalin/src/test/java/io/javalin/TestMultipartForms.kt
@@ -6,7 +6,6 @@
 
 package io.javalin
 
-import io.javalin.http.context.formParam
 import io.javalin.plugin.json.JavalinJackson
 import io.javalin.testing.TestUtil
 import io.javalin.testing.UploadInfo
@@ -163,7 +162,7 @@ class TestMultipartForms {
             val foosExtractedManually = ctx.formParamMap()["foo"]
             val foos = ctx.formParams("foo")
             val bar = ctx.formParam("bar")
-            val baz = ctx.formParam<String>("baz").getOrDefault("default")
+            val baz = ctx.formParamAsClass<String>("baz").getOrDefault("default")
             ctx.result("foos match: " + (foos == foosExtractedManually) + "\n"
                     + "foo: " + foos.joinToString(", ") + "\n"
                     + "bar: " + bar + "\n"
diff --git a/javalin/src/test/java/io/javalin/TestRequest.kt b/javalin/src/test/java/io/javalin/TestRequest.kt
index 6eee528a..d3173a99 100644
--- a/javalin/src/test/java/io/javalin/TestRequest.kt
+++ b/javalin/src/test/java/io/javalin/TestRequest.kt
@@ -9,9 +9,6 @@ package io.javalin
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.security.BasicAuthFilter
 import io.javalin.core.util.Header
-import io.javalin.http.context.formParam
-import io.javalin.http.context.header
-import io.javalin.http.context.queryParam
 import io.javalin.http.staticfiles.Location
 import io.javalin.http.util.ContextUtil
 import io.javalin.testing.TestUtil
@@ -149,7 +146,7 @@ class TestRequest {
 
     @Test
     fun `queryParam defaults to default value`() = TestUtil.test { app, http ->
-        app.get("/") { ctx -> ctx.result("" + ctx.queryParam<String>("qp").getOrDefault("default")) }
+        app.get("/") { ctx -> ctx.result("" + ctx.queryParamAsClass<String>("qp").getOrDefault("default")) }
         assertThat(http.getBody("/")).isEqualTo("default")
     }
 
@@ -194,7 +191,7 @@ class TestRequest {
 
     @Test
     fun `formParam returns defaults to default value`() = TestUtil.test { app, http ->
-        app.post("/") { ctx -> ctx.result("" + ctx.formParam<Int>("fp4").getOrDefault(4)) }
+        app.post("/") { ctx -> ctx.result("" + ctx.formParamAsClass<Int>("fp4").getOrDefault(4)) }
         assertThat(http.post("/").body("fp1=1&fp2=2").asString().body).isEqualTo("4")
     }
 
@@ -356,7 +353,7 @@ class TestRequest {
 
     @Test
     fun `validator header works`() = TestUtil.test { app, http ->
-        app.get("/") { it.result(it.header<Double>("double-header").get().javaClass.name) }
+        app.get("/") { it.result(it.headerAsClass<Double>("double-header").get().javaClass.name) }
         assertThat(http.getBody("/", mapOf("double-header" to "12.34"))).isEqualTo("java.lang.Double")
     }
 }
diff --git a/javalin/src/test/java/io/javalin/TestValidation.kt b/javalin/src/test/java/io/javalin/TestValidation.kt
index 766442a4..b732e531 100644
--- a/javalin/src/test/java/io/javalin/TestValidation.kt
+++ b/javalin/src/test/java/io/javalin/TestValidation.kt
@@ -16,10 +16,6 @@ import io.javalin.core.validation.ValidationError
 import io.javalin.core.validation.ValidationException
 import io.javalin.core.validation.Validator
 import io.javalin.core.validation.collectErrors
-import io.javalin.http.context.bodyValidator
-import io.javalin.http.context.formParam
-import io.javalin.http.context.pathParam
-import io.javalin.http.context.queryParam
 import io.javalin.plugin.json.JavalinJackson
 import io.javalin.testing.SerializableObject
 import io.javalin.testing.TestUtil
@@ -34,19 +30,19 @@ class TestValidation {
 
     @Test
     fun `pathParam gives correct error message`() = TestUtil.test { app, http ->
-        app.get("/:param") { ctx -> ctx.pathParam<Int>("param").get() }
+        app.get("/:param") { ctx -> ctx.pathParamAsClass<Int>("param").get() }
         assertThat(http.get("/abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
     }
 
     @Test
     fun `queryParam gives correct error message`() = TestUtil.test { app, http ->
-        app.get("/") { ctx -> ctx.queryParam<Int>("param").get() }
+        app.get("/") { ctx -> ctx.queryParamAsClass<Int>("param").get() }
         assertThat(http.get("/?param=abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
     }
 
     @Test
     fun `formParam gives correct error message`() = TestUtil.test { app, http ->
-        app.post("/") { ctx -> ctx.formParam<Int>("param").get() }
+        app.post("/") { ctx -> ctx.formParamAsClass<Int>("param").get() }
         assertThat(http.post("/").body("param=abc").asString().body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
         JavalinLogger.enabled = true
         val log = TestUtil.captureStdOut { http.post("/").body("param=abc").asString().body }
@@ -56,14 +52,14 @@ class TestValidation {
 
     @Test
     fun `notNullOrEmpty works for Validator`() = TestUtil.test { app, http ->
-        app.get("/") { ctx -> ctx.queryParam<String>("my-qp").get() }
+        app.get("/") { ctx -> ctx.queryParamAsClass<String>("my-qp").get() }
         assertThat(http.get("/").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","args":{},"value":null}]}""")
         assertThat(http.get("/").status).isEqualTo(400)
     }
 
     @Test
     fun `notNullOrEmpty works for NullableValidator`() = TestUtil.test { app, http ->
-        app.get("/") { ctx -> ctx.queryParam<String>("my-qp").allowNullable().get() }
+        app.get("/") { ctx -> ctx.queryParamAsClass<String>("my-qp").allowNullable().get() }
         assertThat(http.get("/").body).isEqualTo("")
         assertThat(http.get("/").status).isEqualTo(200)
     }
@@ -71,7 +67,7 @@ class TestValidation {
     @Test
     fun `getAs clazz works`() = TestUtil.test { app, http ->
         app.get("/int") { ctx ->
-            val myInt = ctx.queryParam<Int>("my-qp").get()
+            val myInt = ctx.queryParamAsClass<Int>("my-qp").get()
             ctx.result((myInt * 2).toString())
         }
         assertThat(http.get("/int").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","args":{},"value":null}]}""")
@@ -82,7 +78,7 @@ class TestValidation {
     @Test
     fun `check works`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            ctx.queryParam<String>("my-qp").check({ it.length > 5 }, "Length must be more than five").get()
+            ctx.queryParamAsClass<String>("my-qp").check({ it.length > 5 }, "Length must be more than five").get()
         }
         assertThat(http.get("/?my-qp=1").body).isEqualTo("""{"my-qp":[{"message":"Length must be more than five","args":{},"value":"1"}]}""")
     }
@@ -90,7 +86,7 @@ class TestValidation {
     @Test
     fun `default query param values work`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            val myInt = ctx.queryParam<Int>("my-qp").getOrDefault(788)
+            val myInt = ctx.queryParamAsClass<Int>("my-qp").getOrDefault(788)
             ctx.result(myInt.toString())
         }
         assertThat(http.get("/?my-qp=a").body).isEqualTo("""{"my-qp":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"a"}]}""")
@@ -100,7 +96,7 @@ class TestValidation {
 
     @Test
     fun `unregistered converter fails`() = TestUtil.test { app, http ->
-        app.get("/duration") { it.queryParam<Duration>("from").get() }
+        app.get("/duration") { it.queryParamAsClass<Duration>("from").get() }
         assertThat(http.get("/duration?from=abc").status).isEqualTo(500)
     }
 
@@ -110,8 +106,8 @@ class TestValidation {
     fun `custom converter works`() = TestUtil.test(Javalin.create { it.jsonMapper(timeModuleMapper) }) { app, http ->
         JavalinValidation.register(Instant::class.java) { Instant.ofEpochMilli(it.toLong()) }
         app.get("/instant") { ctx ->
-            val fromDate = ctx.queryParam<Instant>("from").get()
-            val toDate = ctx.queryParam<Instant>("to")
+            val fromDate = ctx.queryParamAsClass<Instant>("from").get()
+            val toDate = ctx.queryParamAsClass<Instant>("to")
                 .check({ it.isAfter(fromDate) }, "'to' has to be after 'from'")
                 .get()
             ctx.json(toDate.isAfter(fromDate))
@@ -124,8 +120,8 @@ class TestValidation {
     fun `custom converter works for null when nullable`() = TestUtil.test { app, http ->
         JavalinValidation.register(Instant::class.java) { Instant.ofEpochMilli(it.toLong()) }
         app.get("/instant") { ctx ->
-            val fromDate = ctx.queryParam<Instant>("from").get()
-            val toDate = ctx.queryParam<Instant>("to")
+            val fromDate = ctx.queryParamAsClass<Instant>("from").get()
+            val toDate = ctx.queryParamAsClass<Instant>("to")
                 .allowNullable()
                 .check({ it == null || it.isAfter(fromDate) }, "'to' has to null or after 'from'")
                 .get()
@@ -138,7 +134,7 @@ class TestValidation {
     @Test
     fun `custom converter returns null`() = TestUtil.test { app, http ->
         JavalinValidation.register(Instant::class.java) { null }
-        app.get("/instant") { it.queryParam<Instant>("from").get() }
+        app.get("/instant") { it.queryParamAsClass<Instant>("from").get() }
         assertThat(http.get("/instant?from=1262347200000").status).isEqualTo(400)
     }
 
@@ -194,7 +190,7 @@ class TestValidation {
     @Test
     fun `custom treatment for ValidationException exception response works`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            val myString = ctx.queryParam<String>("my-qp").get()
+            val myString = ctx.queryParamAsClass<String>("my-qp").get()
         }
         app.exception(ValidationException::class.java) { e, ctx ->
             ctx.status(HttpStatus.EXPECTATION_FAILED_417)
@@ -206,14 +202,14 @@ class TestValidation {
 
     @Test
     fun `allowNullable throws if called after check`() = TestUtil.test { app, http ->
-        app.get("/") { it.queryParam<Int>("my-qp").check({ false }, "Irrelevant").allowNullable() }
+        app.get("/") { it.queryParamAsClass<Int>("my-qp").check({ false }, "Irrelevant").allowNullable() }
         assertThat(http.get("/").status).isEqualTo(500)
     }
 
     @Test
     fun `optional query param value works`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            val myInt: Int? = ctx.queryParam<Int>("my-qp").allowNullable().get()
+            val myInt: Int? = ctx.queryParamAsClass<Int>("my-qp").allowNullable().get()
             assertThat(myInt).isEqualTo(null)
         }
         assertThat(http.get("/").status).isEqualTo(200)
@@ -222,7 +218,7 @@ class TestValidation {
     @Test
     fun `optional query param value with check works`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            val id: Int? = ctx.queryParam<Int>("id")
+            val id: Int? = ctx.queryParamAsClass<Int>("id")
                 .allowNullable()
                 .check({ if (it != null) it > 10 else true }, "id was not greater than 10")
                 .get()
@@ -255,11 +251,11 @@ class TestValidation {
     fun `All errors can be collected from multiple validators`() = TestUtil.test { app, http ->
 
         app.get("/") { ctx ->
-            val numberValidator = ctx.queryParam<Int>("number")
+            val numberValidator = ctx.queryParamAsClass<Int>("number")
                 .check({ it > 12 }, "must be greater than 12.")
                 .check({ it.rem(2) == 0 }, "must be even.")
 
-            val stringValidator = ctx.queryParam<String>("first_name")
+            val stringValidator = ctx.queryParamAsClass<String>("first_name")
                 .check({ !it.contains("-") }, "cannot contain hyphens.")
                 .check({ it.length < 10 }, "cannot be longer than 10 characters.")
 
@@ -315,7 +311,7 @@ class TestValidation {
     @Test
     fun `error args work`() = TestUtil.test { app, http ->
         app.get("/args") { ctx ->
-            ctx.queryParam<Int>("my-qp")
+            ctx.queryParamAsClass<Int>("my-qp")
                 .check({ it > 5 }, ValidationError("OVER_LIMIT", args = mapOf("limit" to 5)))
                 .get()
         }
@@ -325,7 +321,7 @@ class TestValidation {
     @Test
     fun `localization is easy`() = TestUtil.test { app, http ->
         app.get("/") { ctx ->
-            ctx.queryParam<Int>("number")
+            ctx.queryParamAsClass<Int>("number")
                 .check({ it in 6..9 }, ValidationError("NUMBER_NOT_IN_RANGE", args = mapOf("min" to 6, "max" to 9)))
                 .get()
         }
diff --git a/javalin/src/test/java/io/javalin/TestValidation_Java.java b/javalin/src/test/java/io/javalin/TestValidation_Java.java
index c5481617..9616ace4 100644
--- a/javalin/src/test/java/io/javalin/TestValidation_Java.java
+++ b/javalin/src/test/java/io/javalin/TestValidation_Java.java
@@ -18,7 +18,7 @@ public class TestValidation_Java {
     @Test
     public void validation_on_context_works_from_java() {
         TestUtil.test((app, http) -> {
-            app.get("/validate", ctx -> ctx.queryParam("param", Integer.class).get());
+            app.get("/validate", ctx -> ctx.queryParamAsClass("param", Integer.class).get());
             assertThat(http.getBody("/validate?param=hmm")).contains("TYPE_CONVERSION_FAILED");
         });
     }
@@ -26,7 +26,7 @@ public class TestValidation_Java {
     @Test
     public void default_values_work_from_java() {
         TestUtil.test((app, http) -> {
-            app.get("/validate", ctx -> ctx.result(ctx.queryParam("param", Integer.class).getOrDefault(250).toString()));
+            app.get("/validate", ctx -> ctx.result(ctx.queryParamAsClass("param", Integer.class).getOrDefault(250).toString()));
             assertThat(http.getBody("/validate?param=hmm")).contains("TYPE_CONVERSION_FAILED");
             assertThat(http.getBody("/validate")).isEqualTo("250");
         });
diff --git a/javalin/src/test/java/io/javalin/TestWebSocket.kt b/javalin/src/test/java/io/javalin/TestWebSocket.kt
index 8208d79a..d71c1389 100644
--- a/javalin/src/test/java/io/javalin/TestWebSocket.kt
+++ b/javalin/src/test/java/io/javalin/TestWebSocket.kt
@@ -15,7 +15,6 @@ import io.javalin.testing.SerializableObject
 import io.javalin.testing.TestUtil
 import io.javalin.testing.TypedException
 import io.javalin.websocket.WsContext
-import io.javalin.websocket.context.message
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.websocket.api.MessageTooLargeException
 import org.eclipse.jetty.websocket.api.StatusCode
@@ -158,7 +157,7 @@ class TestWebSocket {
         app.ws("/message") { ws ->
             ws.onMessage { ctx ->
                 receivedJson = ctx.message()
-                receivedMessage = ctx.message<SerializableObject>()
+                receivedMessage = ctx.messageAsClass<SerializableObject>()
                 ctx.send(serverMessage)
             }
         }