rosencrantz
rosencrantz copied to clipboard
Feature Request: flat path handler
It would be nice to have a way to handle paths in a flatter way, so no need to nest pathChunk and segment handlers.
Here is a very simple example. I am not too versed with macros or what else Nim can provide. It would be ideal to be able to use a proc with any amount of defined params, or maybe a custom table function that can access the param like a map in jester (@"somepathvar")
import asynchttpserver, asyncdispatch, rosencrantz, sequtils, strutils, sugar
proc pathParse*(s: string, p: proc(params: seq[string]): Handler ): Handler =
proc h(req: ref Request, ctx: Context): Future[Context] {.async.} =
let expectedParts = s.split("/")
let requestedParts = req.url.path.split("/")
var params = newSeq[string]()
if len(expectedParts) != len(requestedParts):
return ctx.reject()
for (expected, requested) in zip(expectedParts,requestedParts):
if expected.startsWith("@"):
params.add(requested)
continue
if expected != requested:
return ctx.reject()
let handler = p(params)
let newCtx = await handler(req,ctx)
return newCtx
return h
let handler = get[(pathParse("/a/@b/c", (parts) => ok(parts[0])))] ~
post[(pathParse("/",(parts) => ok"posted"))]
let server = newAsyncHttpServer()
waitFor server.serve(Port(8080), handler)
In general, the design of rosencrantz encourages composition of handlers, but yours is a nice addition - sometimes it can just be more convenient to parse the whole path at once. Maybe p
could take a table instead of a seq, in order to access parameters by name, like this:
import asynchttpserver, asyncdispatch, rosencrantz, sequtils, strutils, sugar, tables
proc pathParse*(s: string, p: proc(params: TableRef[string, string]): Handler): Handler =
proc h(req: ref Request, ctx: Context): Future[Context] {.async.} =
let expectedParts = s.split("/")
let requestedParts = req.url.path.split("/")
var params = newTable[string, string]()
if len(expectedParts) != len(requestedParts):
return ctx.reject()
for (expected, requested) in zip(expectedParts,requestedParts):
if expected.startsWith("@"):
params[expected[1 .. high(expected)]] = requested
continue
if expected != requested:
return ctx.reject()
let handler = p(params)
let newCtx = await handler(req,ctx)
return newCtx
return h
let handler =
get[(pathParse("/a/@b/c", (parts) => ok(parts["b"])))] ~
post[(pathParse("/", (parts) => ok("posted")))]
let server = newAsyncHttpServer()
waitFor server.serve(Port(8080), handler)
Actually, one should split only the remaning part of the path - you can use ctx.position
to see the part of the path that has yet to be parsed. This would allow nesting this handler together with the other ones.
Would you mind opening a PR and adding a test and mention the new handler in the README?
Sure I will work on a PR.