- Pods
- AF-BedSheet 0.0.2
- API
- RouteSource
- Src
sourceafBedSheet::RouteSource.fan
using web
using afIoc::Inject
**
** Router handles routing URIs to method handlers.
**
// Maybe abstract this away so routing becomes pluggable?
const class RouteSource {
const Route[] routes
@Inject @Config { id="afBedSheet.welcomePage" }
private const Uri? welcomePage
new make(Route[] routes, |This|? in := null) {
in?.call(this)
dups := Route[,]
routes.each |route| {
dup := dups.find { route.routeBase.toStr.equalsIgnoreCase(it.routeBase.toStr) && route.httpMethod == it.httpMethod }
if (dup != null)
throw BedSheetErr(BsMsgs.routeAlreadyAdded(dup.routeBase, dup.handler))
dups.add(route)
}
innies := Route[,]
dups.each |route| {
innies.each {
if (route.routeBase != it.routeBase) {
if (route.routeBase.toStr.startsWith(it.routeBase.toStr))
throw BedSheetErr(BsMsgs.routesCanNotBeNested(route.routeBase, it.routeBase))
if (it.routeBase.toStr.startsWith(route.routeBase.toStr))
throw BedSheetErr(BsMsgs.routesCanNotBeNested(it.routeBase, route.routeBase))
}
}
innies.add(route)
}
this.routes = innies
// validate welcome page uri
if (welcomePage != null)
verify := Route(welcomePage, #toStr)
}
** Match a request uri to Route.
internal RouteMatch match(Uri modRel, Str httpMethod) {
normalisedUri := normalise(modRel)
return routes.eachWhile{ it.match(normalisedUri, httpMethod) }
?: throw RouteNotFoundErr(BsMsgs.routeNotFound(modRel))
// TODO: if req uri is '/' and no routes have been defined, route to a default 'BedSheet Welcome' page
}
private Uri normalise(Uri uri) {
if (uri.path.isEmpty) {
if (welcomePage == null)
throw RouteNotFoundErr(BsMsgs.routeNotFound(uri))
uri = welcomePage
}
if (!uri.isPathAbs)
uri = `/` + uri
return uri
}
}
internal const class RouteMatch {
const Uri routeBase
const Uri routeRel
const Str httpMethod
const Method handler
new make(Uri routeBase, Uri routeRel, Str httpMethod, Method handler) {
this.routeBase = routeBase
this.routeRel = routeRel
this.httpMethod = httpMethod
this.handler = handler
}
Str[] argList() {
routePath := routeRel.path
if (handler.params.size == routePath.size)
return routePath
paramRange := (handler.params.findAll { !it.hasDefault }.size..<handler.params.size)
if (paramRange.contains(routePath.size))
return routePath
throw RouteNotFoundErr(BsMsgs.handlerArgSizeMismatch(handler, routeRel))
}
}