ktor icon indicating copy to clipboard operation
ktor copied to clipboard

Add optionalRoute

Open goncalossilva opened this issue 2 years ago • 3 comments

Subsystem Server.

Motivation Optional route allows a path segment to optionally exist.

One example where this is useful is in API versioning and rerouting “missing version” to the latest version without a redirect:

fun Application.installRoutes() = routing {
    optionalRoute("v1") {
        // ...
    }
}

In this case, both api.domain.com/v1/some-route and api.domain.com/some-route would point to the same place.

Solution This is a draft PR, lifted from Doist/ffs. I'm happy to add a ticket in YT, review the structure for a tighter integration, add tests, and so forth, but would prefer determining if there's interest in this first.

goncalossilva avatar Feb 04 '22 11:02 goncalossilva

Hi @goncalossilva We already have functionaluty for optional path parameters. But as I understand, you need optional constant path segments, right? For path parameters you can use this code

    @Test
    fun testOptionalPathParameter() = testApplication {
        routing {
            route("{test?}") {
                handle { call.respond(call.parameters["test"] ?: "missing") }
            }
        }

        assertEquals("asd", client.get("/asd").bodyAsText())
        assertEquals("missing", client.get("/").bodyAsText())
    }

rsinukov avatar Feb 07 '22 14:02 rsinukov

Exactly. The issue with optional path parameters is that they can only be used at the end of a path, while an optional route can be set anywhere in the path.

Thinking back to the API example I alluded to in the original post, both of these could work and point to the same route:

api.domain.com/v1/organizations/1/edit
api.domain.com/organizations/1/edit

A test could be:

@Test
fun testOptionalRouteParameter() = testApplication {
    routing {
        optionalRoute("optional") {
            route("test") {
                handle { call.respond("here") }
            }
        }
    }

    assertEquals("here", client.get("/optional/test").bodyAsText())
    assertEquals("here", client.get("/test").bodyAsText())
}

goncalossilva avatar Feb 07 '22 17:02 goncalossilva

Hey @goncalossilva, thanks for the PR. Could you add some tests and run ./gradlew apiDump and commit changes?

e5l avatar Jan 05 '24 10:01 e5l