Skip to content

Nesting path combinators #570

@slogsdon

Description

@slogsdon

As a newcomer to Suave, I've been working to move past copying and pasting examples I come across, and I've started building an application from scratch. During this process, I attempted to do something like this:

let app =
  path "/" >=> OK "index"
  path "/api"
    >=> choose [
          path "" >=> NO_CONTENT
          path "/users" >=> OK """{"api": "users"}"""
        ]

expecting a request to /api would return a 204 No Content response and a request to /api/users would return the JSON-encoded string. To my surprise, I received a 404 Not Found for both. After doing some digging, I discovered that path tests if the given path matches the request path, so with path "/users" above, it compares a request to /api/users with the given /users, fails, and returns None.

I wasn't able to find anything in the existing code base to handle this, so I added the following as a helper module in my application to achieve the expected behavior:

let optionally pred value =
  if pred then Some value else None

let getCurrentRoot ctx =
  match ctx.userState.TryFind("rootPath") with
  | None -> ""
  | Some p -> string p

let rootPath (part : string) (ctx : HttpContext) =
  let root = getCurrentRoot ctx
  { ctx with userState = ctx.userState.Add("rootPath", root + part) }
  |> Some
  |> async.Return

let subPath (part : string) (ctx : HttpContext) =
  let fullPath = (getCurrentRoot ctx) + part
  ctx
  |> optionally (fullPath = ctx.request.path)
  |> async.Return

allowing the following to work as expected:

let app =
  path "/" >=> OK "index"
  rootPath "/api"
    >=> choose [
          // handles requests to `/api`
          subPath "" >=> NO_CONTENT
          // handles requests to `/api/users`
          subPath "/users" >=> OK """{"api": "users"}"""
        ]

I took to Twitter to see if there was an official way of doing this, and the official Twitter handle suggested I open a PR to start a discussion (see: https://twitter.com/SuaveIO/status/823219104995741696). I'm a a big fan of using issues for these types of discussions, but if something like the above is desired for Suave, I'd be happy to submit a PR for any changes.

Does something like this exist today? If not, would it be useful to include this functionality as a part of Suave?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions