Reputation: 3906
Does the Ktor framework provide a way of accessing a route's path string within a request?
For example, if I set up a route such as:
routing {
get("/user/{user_id}") {
// possible to get the string "/user/{user_id}" here?
val path = someFunction()
assert(path == "/user/{user_id}")
}
}
To clarify, I'm looking for a way to access the unprocessed path string, i.e. "/user/{user_id}"
in this case (accessing the path via call.request.path()
gives me the path after the {user_id}
has been filled in, e.g. "/user/123"
).
I can of course assign the path to a variable and pass it to both get
and use it within the function body, but wondering if there's a way of getting at the route's path without doing that.
Upvotes: 6
Views: 4599
Reputation: 19585
If for some reason you are unable to statically determine your routes as explained by the accepted answer, it is possible to determine the route at runtime via a plugin.
The plugin captures the route information and makees it available to the application as an attribute of the call context. Here is the plugin code:
import io.ktor.server.application.*
import io.ktor.server.application.hooks.*
import io.ktor.server.routing.*
import io.ktor.util.*
val routeKey = AttributeKey<Route>("RequestRoute")
val RequestRoutePlugin = createApplicationPlugin("RequestRoutePlugin") {
on(MonitoringEvent(Routing.RoutingCallStarted)) { call ->
call.attributes.put(routeKey, call.route)
}
}
fun ApplicationCall.requestRoute() = attributes[routeKey]
Once that plugin is installed via install(RequestRoutePlugin)
, simply use call.requestRoute().parent
to access the route you want. For example:
get("/foo/{bar}") {
call.respondText(
"bar=${call.parameters["bar"]} route=${call.requestRoute().parent.toString()}",
status = HttpStatusCode.OK
)
}
will respond to a request for /foo/abc
as:
bar=abc route=/foo/{bar}
Upvotes: 2
Reputation: 11
fun Route.fullPath(): String {
val parentPath = parent?.fullPath() ?: "/"
return when (selector) {
is TrailingSlashRouteSelector,
is AuthenticationRouteSelector,
is HttpMethodRouteSelector,
is HttpHeaderRouteSelector,
is HttpAcceptRouteSelector -> parentPath
else -> parentPath.let { if (it.endsWith("/")) it else "$it/" } + selector.toString()
}
}
route("/user/{id}", HttpMethod.Get) {
handle {
val path = [email protected]()
println(path)
}
}
Upvotes: 1
Reputation: 168
So all solutions so far miss the most obvious (and in my humble opinion - correct) way to extract the path variables:
routing {
get("/user/{user_id}") {
val userId = call.parameters["user_id"]
}
}
call.parameters["user_id"] will return a value of type String?
You can have multiple path variables and pull them out by this method.
Upvotes: -3
Reputation: 19
I found a solution for this problem
val uri = "foos/foo"
get("$uri/{foo_id}") {
val path = call.request.path()
val firstPart = path.length
val secondPart = path.slice((firstPart+1) until path.length)
call.respondText("$secondPart")
}
try this code it's simple and robust
Upvotes: -1
Reputation: 459
I solved it like this
// Application.kt
private object Paths {
const val LOGIN = "/login"
...
}
fun Application.module(testing: Boolean = false) {
...
routing {
loginGet(Paths.LOGIN)
}
}
And to structure my extension functions, I put them in other files like this
// Auth.kt
fun Route.loginGet(path: String) = get(path) {
println("The path is: $path")
}
Upvotes: 1
Reputation: 8457
I don't think that is possible. What you could do instead is write such a class/object
object UserRoutes {
const val userDetails = "/users/{user_id}"
...
}
And reference that field from your routing module:
import package.UserRoutes
get(UserRoutes.userDetails) {...}
By doing so you would need to just reference that string from the given singleton. Also no need for the object
wrapper but I think it looks neat that you can group the paths by somewhat their module name
Upvotes: 2