Skip to content

Conversation

@AugustoRengel
Copy link
Contributor

Description

This PR extends the existing routeBind support from the Giraffe.Routing module to also work with Giraffe.EndpointRouting, while preserving the same syntax.

A new test was added to EndpointRoutingTests.fs to validate the routeBind behavior when using endpoint routing.

How to test

  1. Define an endpoint using routeBind, for example:
open Giraffe.EndpointRouting

[<CLIMutable>]
type Name = { First: string; Last: string }

[<CLIMutable>]
type Person = { Name: Name; Age: int }

let endpoints: Endpoint list =
    [
        GET [
            routeBind<Person>
                "/p/{Name.First}/{Name.Last}/{Age}"
                (fun (person: Person) ->
                    text ($"Name.First: {person.Name.First}, Name.Last: {person.Name.Last}, Age: {person.Age}")
                )
        ]
    ]
  1. Run the application.
  2. Send a request such as: GET /p/John/Doe/32
  3. Verify that:
    • The request is successfully routed.
    • The handler receives a Person instance with First = "John", Last = "Doe", and Age = 32.

Also, a new test was added to EndpointRoutingTests.fs

Related issues

Fixes #708

@AugustoRengel
Copy link
Contributor Author

The documentation mentions that endpoints ending with a trailing "/" can be covered by adding the "(/?)" regex, for example: routeBind<Blah> "/p/{foo}/{bar}(/?)" blahHandler.
However, this is not necessary when using Giraffe.EndpointRouting, as ASP.NET Core already handles optional trailing slashes.
Therefore, this part of the documentation should be updated accordingly. I will do so once I have a more complete understanding of the exact behavior.

@AugustoRengel
Copy link
Contributor Author

I could not find any explicit mention of this behavior in the documentation, but after reviewing the source code it appears that the request path is split into segments and empty segments are ignored.
The class responsible for this behavior seems to be DfaMatcher.

Based on this, I am going to update the documentation to confirm that no special handling is required for a trailing “/” when using Endpoint Routing. Please let me know if I am missing something.

@AugustoRengel AugustoRengel marked this pull request as ready for review January 13, 2026 22:22
Copy link
Member

@64J0 64J0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR @AugustoRengel, thanks for this contribution!
It's fine to add this new handler to the EndpointRouting module, I think.

Copy link
Member

@64J0 64J0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After taking a better look at the code.

Comment on lines 292 to 294
match ModelParser.tryParse<'T> None routeData with
| Ok model -> handler model next ctx
| Error _ -> RequestErrors.BAD_REQUEST "Failed to bind route parameters" next ctx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it's better to not return this RequestErrors.BAD_REQUEST "Failed to bind route parameters" message if the parse operation fails.

Instead, we need to just keep going, testing other routes, and eventually using the error handler specified by the client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the error with skipPipeline, which allows the request to flow to the next handler, but it does not switch to another endpoint because the model parsing happens after endpoint selection.

To achieve the desired behavior, the model parsing would need to run before the endpoint selection phase. Based on the ASP.NET Core routing pipeline, this seems only possible by using a custom MatcherPolicy.

If there is any alternative or recommended approach to achieve this, please let me know if I am missing something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking out loud.

Something we do in other places of the project is to let our clients define a special error handler.

For example: #691.

Perhaps it makes sense to use this approach here.

Anyway, we can add this optional error handler later.

@64J0 64J0 requested review from TheAngryByrd and nojaf January 14, 2026 01:30
@64J0
Copy link
Member

64J0 commented Jan 14, 2026

Adding @nojaf and @TheAngryByrd for additional review and feedback.

Copy link
Contributor

@nojaf nojaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me from afar.

@AugustoRengel AugustoRengel requested a review from 64J0 January 14, 2026 17:59
@64J0 64J0 merged commit 49f120d into giraffe-fsharp:master Jan 26, 2026
6 checks passed
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

Successfully merging this pull request may close these issues.

Updates related to routeBind

3 participants