Falco icon indicating copy to clipboard operation
Falco copied to clipboard

Endpoint routing not working with .net 6 preview 6 minial api swagger integration

Open albertwoo opened this issue 4 years ago • 34 comments

I am trying falco with swagger. Because recently I read some post that swaggert now support .net 6 endpoint routing start from preview 6. But unfortuatly it is not working with falco endpoint.

Below is the demo code:

open System
open System.Threading.Tasks
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.DependencyInjection
open Falco
open Falco.Routing


let builder = WebApplication.CreateBuilder()

builder.Services.AddEndpointsApiExplorer()
builder.Services.AddHttpContextAccessor() |> ignore
builder.Services.AddSwaggerGen() |> ignore

let application = builder.Build()

application.UseSwagger() |> ignore


application.UseFalcoEndpoints [
    get "/" (Response.ofPlainText "Hello World")
]

// not working
// falco will call toRequestDelegate to convert handler into RequestDelegate which will erase the return type wrapped in Task
// if we can find a way to change to Func<_, _> style with more type info then we can integrate with swagger
application.MapGet("/hello1", RequestDelegate(fun (ctx: HttpContext) -> Task.FromResult $"Hi {ctx.Request.Path}" :> Task)) |> ignore

// working
application.MapGet("/hello2", Func<_, _>(fun (ctx: HttpContext) -> $"Hi {ctx.Request.Path}")) |> ignore

application.UseSwaggerUI() |> ignore

application.Run()

albertwoo avatar Jul 20 '21 13:07 albertwoo

What happens when you use the webhost expression?

pimbrouwers avatar Jul 20 '21 13:07 pimbrouwers

It should be the same, because at first I tried that. And find it does not work so I switch to minial api style.

albertwoo avatar Jul 21 '21 01:07 albertwoo

Related comment here that touches on why no F# library (Falco/Giraffe/etc) should expect the minimal API syntax to work for us out of the box. I had a conversation the other day where I touched on it in slightly more detail if you want to hear more as well.

baronfel avatar Jul 22 '21 13:07 baronfel

Any news on this front? swashbuckle is still a missing integration (and painpoint) for all F# web frameworks? has anyone yet tried to just "write" a custom openapi json object based on the registered endpoints, and send it to swashbuckle default endpoint for schema? like https://petstore.swagger.io/v2/swagger.json

can be served by serverside in F#

     get "v2/swagger.json" generateOpenapiSpecBasedOnRegisteredRoutes()

maybe using also this lib to generate it, or more simly json or yaml type providers with a rich default template file for the schema?

https://github.com/microsoft/OpenAPI.NET

?

jkone27 avatar Dec 21 '21 12:12 jkone27

I have literally no experience with Swashbuckle. I don't deliver formatted APIs professionally. So I am willing to assist anyone who wants to take the lead on this.

pimbrouwers avatar Dec 21 '21 14:12 pimbrouwers

https://github.com/domaindrivendev/Swashbuckle.AspNetCore

i think with swashbuckle this would be sufficient (can also be pointed to a different custom endpoint if required), and then we need to generate and serve the openapi.json content, to make the swashbuckle UI happy.

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("v2/swagger.json", "My API V2");
});

the not super easy part is how to get a list/description of registered endpoints with verbs, routes and models etc.

mainly we would have to generate the corresponding OpenApi spec file (e.g. see) https://editor.swagger.io/, based on registered endpoints/routes in falco (but would/could be nicer if it's general and can be re-used also in Giraffe and Saturn!

example:

https://petstore.swagger.io/ needs to have Openapi document served at https://petstore.swagger.io/v2/swagger.json to correctly generate the ui,that's all done by swashbuckle package (no work to do there),

we just need to generate the correct openapi spec

jkone27 avatar Dec 21 '21 16:12 jkone27

i think i found a way to "read endpoints" , at least in saturn if using endpoints routing (not sure if falco also already uses endpoints)

image

this is a start to dynamically create the openapi definition :)

jkone27 avatar Dec 23 '21 09:12 jkone27

there also seems to be this project, which is interesting https://github.com/akhansari/FSharp.OpenApi

jkone27 avatar Dec 23 '21 11:12 jkone27

Nice work. Falco definitely uses endpoints. It even produces its own endpoint data source, as per the MSFT specs: https://github.com/pimbrouwers/Falco/blob/e4ac1fa2cb970901462722785d3cb95de00c59f4/src/Falco/Routing.fs#L19

I'll have a closer look at all of this when I'm back from my holiday.

pimbrouwers avatar Dec 23 '21 12:12 pimbrouwers

!! Thanks @pimbrouwers 👍 🎅 🎄 :) :) enjoy holidays

jkone27 avatar Dec 23 '21 12:12 jkone27

You as well!!

pimbrouwers avatar Dec 23 '21 12:12 pimbrouwers

Getting the data source hasn't ever really been the problem here. The problem is in generating the OpenApi spec in a way that plugs in nicely with other ASP.NET Core middleware/libraries/etc. Currently, Swashbuckle builds the OpenAPI description from an ApiDescription, which ASP.NET Core builds itself via a Builder. This builder expects a certain shape of the data returned and so we'd need to either replicate that shape (hard) or provide alternate means of deriving these same shapes (might also be hard). Hope this helps.

baronfel avatar Dec 23 '21 15:12 baronfel

and what also stucks me is getting type information.. as there i need somehow to get a Expression or quotation from a RequestDelegate ...

let getApiInformations (context: HttpContext) =
        
        let endpoints = context.GetService<IEnumerable<EndpointDataSource>>()
        
        let endpoint = endpoints |> Seq.head
        
        let endpointsInformation = 
            [ for m in endpoint.Endpoints.OfType<RouteEndpoint>() do
                let httpMethodMetadata = m.Metadata.[0] :?> HttpMethodMetadata
                let name = m.DisplayName //path HTTP: VERB
                //let arguments = m.RequestDelegate. TODO for Types
                yield { DisplayName = name; Metadata = httpMethodMetadata; }
            ]
            
        { Endpoints = endpointsInformation }

could only get this up to now ..

image

:D i know it probably looks funny to whom is experienced already 😀

probably the best way would be that the API library has a routing table with IN and OUT types etc.. that can be "queried" to build the openapi spec.

will def check that builder code 👍

jkone27 avatar Dec 23 '21 16:12 jkone27

it seems methodinfo has to be extracted from routeEndpoint.RequestDelegate in case of giraffe/saturn.

if only this could be invoked externally to not rely on MVC only code.. but it's private...

private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string httpMethod, MethodInfo methodInfo)
    {

jkone27 avatar Dec 23 '21 16:12 jkone27

yeah, that's why the built-in stuff doesn't work for any of the F# frameworks except those that use raw minimal APIs. the frameworks have to develop their own ways of reporting the metadata to the api description providers in order to plug in. it's a sad state of affairs, and will invariably lead to a lot of re-work :(

baronfel avatar Dec 23 '21 16:12 baronfel

is there any way of ending up anywhere if we try re-write it completely without re-using the CreateApiDescription code from swashbuckle? for sure is quite many lines of code but on the other hand the "code is available", so maybe would be worth giving it a try for the F# community (else this whole openapi thing stays stuck forever as it has been for many years already?)

Suave is the only one having a working integration atm (as not relying on aspnetcore...) https://rflechner.github.io/Suave.Swagger/

jkone27 avatar Jan 10 '22 17:01 jkone27

seems there is some work on from aspnet for NET7 milestone.. but it might take time.. https://github.com/dotnet/aspnetcore/issues/37098

jkone27 avatar Jan 14 '22 13:01 jkone27

seems there was some progress on NET7 aspnet preview 2, maybe we could start trying it on a branch to see if it does work as expected :) https://devblogs.microsoft.com/dotnet/announcing-dotnet-7-preview-2/

jkone27 avatar Apr 05 '22 20:04 jkone27

Apologies, I haven't been keeping a close enough eye on this.

I will create a branch for us.

pimbrouwers avatar Aug 20 '22 10:08 pimbrouwers

Is there anything I can help with? It seems quite complicated, but generating an OpenAPI spec is a big part of getting people onboard with F# at $WORK for a new service

zetashift avatar Mar 09 '23 20:03 zetashift