-
Notifications
You must be signed in to change notification settings - Fork 3
Routing
Based on Ktor Routing
sourceSets {
commonMain.dependencies {
implementation("dev.programadorthi.routing:core:$version")
}
}
All route definition provided by Ktor Routing is supported by Kotlin Routing.
val router = routing {
handle("/hello") {
// Handle any call to the "/hello" route
}
}
It's also possible to define a name to navigate instead of using the path value.
handle(path = "/hello", name = "hello") {
// ...
}
Well, there is no Get, Post and no other
HttpMethod
related verb functions, of course.
So to distinguish some route from another you can create or use the default route methods.
val router = routing {
handle("/hello", method = RouteMethod("PUSH")) {
// Handle any call to the "/hello" route
}
}
The same as oficial docs Specify a path pattern
If you want to define multiple route handlers, which of course is the case for any application, you can just add them to the routing function:
val router = routing {
handle("/customer/{id}") {
// ...
}
handle("/customer") {
// ...
}
handle("/order") {
// ...
}
handle("/order/{id}") {
// ...
}
}
In this case, each route has its own function and responds to the specific route.
An alternative way is to group these by paths, whereby you define the path and then place the handle for that path as nested functions, using the route function:
val router = routing {
route("/customer") {
handle {
// handle call to /customer
}
handle("/{id}") {
// handle call to /customer/id
}
}
route("/order") {
handle {
// handle call to /order
}
handle("/{id}") {
// handle call to /order/id
}
}
}
Kotlin Routing allows you to have sub-routes as parameters to route functions. This can be useful to define resources that are logically children of other resources.
val router = routing {
route("/order") {
route("/shipment") {
handle {
// handle call to /order/shipment
}
handle("/{id}") {
// handle call to /order/shipment/id
}
}
}
}
The same as oficial docs Route extension functions
router.call(uri = "/hello")
router.call(name = "hello")
// by uri
router.call(uri = "/customer/1234")
// by name
router.call(
name = "named",
parameters = parametersOf("id", "1234")
)
// by uri and parameters
router.call(
uri = "/customer",
parameters = parametersOf("id", "1234")
)
Path and Query parameters use the same parameters map to provide and get the values
// by uri
router.call(uri = "/customer?id=1234")
// by name
router.call(
name = "named",
parameters = parametersOf("id", "1234")
)
// by uri and parameters
router.call(
uri = "/customer",
parameters = parametersOf("id", "1234")
)
router.call(
// ...,
routeMethod = RouteMethod("PUSH")
)
Sometimes path and query parameters aren't enough to pass values to the route.
router.callWithBody(
// ...,
body = AnyTypeInstance
)
val router = routing { }
// check current routing only
router.canHandleByPath(path = "/path")
// check current and parent routing when not found on the current
router.canHandleByPath(path = "/path", lookUpOnParent = true)
// check by path and method
router.canHandleByPath(path = "/path", method = RouteMethod("PUSH"), ...)
val router = routing { }
// check current routing only
router.canHandleByName(name = "named")
// check current and parent routing when not found on the current
router.canHandleByName(name = "named", lookUpOnParent = true)
// check by name and method
router.canHandleByName(name = "named", method = RouteMethod("PUSH"), ...)
Kotlin Routing allows you to handle incoming call and perform various actions when handling it.
There is no return or respond* to do as Ktor version does.
Inside a route handler, you can get access to the call using the call
extension property.
val router = routing {
handle("/hello") {
val application = call.application // routing application instance
val routeMethod = call.routeMethod // the called route method
val name = call.name // the called route name
val uri = call.uri // the called route uri
val parameters = call.parameters // Path or query parameters
}
}
This section shows how to receive body contents sent with callWithBody()
.
val router = routing {
handle("/hello") {
val valueOne: AnyTypeInstance = call.receive() // throw an exception whether there is no value to receive
val valueTwo: AnyTypeInstance? = call.receiveNullable() // returns null when there is no value to receive
}
}
To redirect from one route to another call the function:
Change route method or append parameters are supported in redirect functions.
val router = routing {
handle("/hello") {
call.redirectToPath(path = "/path-destination")
}
}
val router = routing {
handle("/hello") {
call.redirectToName(name = "destination-name")
}
}
Routes can be registered on demand avoiding have to declare all of them on Routing block:
val router = routing { }
// on demand
router.handle(...) {
// ...
}
To avoid duplicated route handlers or other unexpected behaviors, sometimes we require to unregister some handlers.
val router = routing { }
router.unregisterPath(path = "/path")
val router = routing { }
router.unregisterNamed(name = "named")
Maybe you have distinct features that have it own lifecycle and know the parent Routing only. This create a hierarchy like a B-Tree that root and leaf knows each other but siblings don't.
val applicationRouter = routing { }
val featureARouter = routing(rootPath = "/feature-a", parent = applicationRouter) {
handle("/hello-a") {
// ...
}
}
val featureBRouter = routing(rootPath = "/feature-b", parent = applicationRouter) {
handle("/hello-b") {
// ...
}
}
val featureCRouter = routing(rootPath = "/feature-c", parent = featureARouter) {
handle("/hello-c") {
// ...
}
}
// routing inside C only
featureCRouter.call(uri = "/feature-c/hello-c")
// routing from A to C
featureARouter.call(uri = "/feature-a/feature-c/hello-c")
// routing from C to A
featureCRouter.call(uri = "/feature-a/hello-a")
// routing from C or A to B throw RouteNotFoundException
(featureCRouter|featureARouter).call(uri = "/feature-b/hello-b")
// routing from application router can go to anyone
applicationRouter.call(uri = "/feature-a/feature-c/hello-c")