Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: flat path handler #26

Open
jivank opened this issue Jan 14, 2020 · 2 comments
Open

Feature Request: flat path handler #26

jivank opened this issue Jan 14, 2020 · 2 comments

Comments

@jivank
Copy link

jivank commented Jan 14, 2020

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)
@andreaferretti
Copy link
Owner

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?

@jivank
Copy link
Author

jivank commented Jan 17, 2020

Sure I will work on a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants