com.natpryce.krouton.http4k.routing.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of krouton Show documentation
Show all versions of krouton Show documentation
Type-safe and compositional URL routing and reverse routing
package com.natpryce.krouton.http4k
import com.natpryce.krouton.PathTemplate
import com.natpryce.krouton.monitoredPath
import com.natpryce.krouton.parse
import com.natpryce.krouton.splitPath
import com.natpryce.krouton.toUrlTemplate
import org.http4k.core.Filter
import org.http4k.core.HttpHandler
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.UriTemplate
import org.http4k.core.then
import org.http4k.routing.RoutedRequest
import org.http4k.routing.RoutedResponse
/**
* A route might map a request and some data parsed from that request to a response.
*/
typealias Route = (Request, T) -> Response?
/**
* The capability to describe its routing as URL templates (a subset of RFC 6570)
*/
interface ReportsUrlTemplates {
fun urlTemplates(): List
}
/**
* A Krouton handler that dispatches to the first element of the `routes` that matches the request,
* and invokes `handlerIfNoMatch` if none of them match.
*/
data class Router>(
val routes: List,
val handlerIfNoMatch: (Request, T) -> Response
) : (Request, T) -> Response {
override fun invoke(request: Request, t: T): Response =
routes.firstNonNull { it(request, t) } ?: handlerIfNoMatch(request, t)
}
/**
* Returns the URL templates of all `routes` if they can report their routing rule as a URL template
*/
fun Router.urlTemplates()
where ROUTE : Route,
ROUTE : ReportsUrlTemplates =
routes.flatMap { it.urlTemplates() }
/**
* A ResourceRouter is an HttpHandler that can route the request to one of its ResourceRoutes
*/
data class ResourceRouter(val router: Router, ResourceRoute>) :
HttpHandler,
ReportsUrlTemplates
{
constructor(routes: List, handlerIfNoMatch: HttpHandler) :
this(Router(routes, { rq, _ -> handlerIfNoMatch(rq) }))
override fun invoke(request: Request): Response {
return router(request, splitPath(request.uri.path))
}
override fun urlTemplates() =
router.urlTemplates()
}
/**
* Apply a filter to all path route handlers in an application
*/
fun ResourceRouter.withFilter(newFilter: Filter) =
copy(router = router.copy(routes = router.routes.map {
it.copy(filter = newFilter.then(it.filter))
}))
/**
* A ResourceRoute that uses Krouton PathTemplates to match paths.
*/
data class PathParsingRoute(
private val pathTemplate: PathTemplate,
private val handler: (Request, T) -> Response,
internal val filter: Filter = Filter { it -> it }
) : Route>, ReportsUrlTemplates {
override fun invoke(request: Request, path: List): Response? =
pathTemplate.parse(path)?.let { parsed ->
val filteredHandler = filter { request -> handler(request, parsed) }
val template = UriTemplate.from(pathTemplate.monitoredPath(parsed))
RoutedResponse(filteredHandler(RoutedRequest(request, template)), template)
}
override fun urlTemplates() = listOf(pathTemplate.toUrlTemplate())
}
typealias ResourceRoute = PathParsingRoute<*>
fun methodHandler(requiredMethod: Method, handler: (Request, T) -> Response): (Request, T) -> Response? =
fun(request: Request, t: T) =
if (request.method == requiredMethod) handler(request, t) else null
private inline fun List.firstNonNull(f: (T) -> U?): U? {
forEach { t -> f(t)?.let { return it } }
return null
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy