Search code examples
c#asp.net-core-webapiminimal-apisfast-endpoints

How to get list of all endpoints in FastEndpoints?


In our app we need to get list of all FastEndpoints endpoints at runtime.

Basically we need the following info for all endpoints;

GroupName (null if not belongs to any group)

EndpointName (Name of endpoint class)

Route (including any route prefixes if applied)

HttpMethod

Previously we were obtaining same kind of information for ApiControllers by enumerating ControllerActionDescriptor type of IActionDescriptorCollectionProvider.

FastEndpoints has EndPointData class for internally scanning endpoints when UseFastEndpoints is called, but this class is internal and can not be used outside.


Solution

  • FastEndpoints is built on top of ASP.NET Core and uses the same routing middleware. Looking at the MapEndpoints source we see it uses the same methods as Minimal APIs to register endpoints and actions :

    foreach (var verb in def.Verbs)
    {
        var hb = app.MapMethods(
            finalRoute,
            [verb],
            (HttpContext ctx, [FromServices] IEndpointFactory factory) => RequestHandler.Invoke(ctx, factory));
    
        hb.WithName(..._;
        ....
        hb.WithMetadata(def);
        ...
    

    This means the actual problem becomes finding all the routes in an ASP.NET Core applications. Gérald Barré's How to list all routes in an ASP.NET Core application article shows how to do that for all flavors of ASP.NET Core (Minimal API, Controllers, Razor Pages, gRPC etc). All endpoints are registered as EndpointDataSource objects. To retrieve all of them, we can request IEnumerable<EndpointDataSource> :

    ...
    if (app.Environment.IsDevelopment())
    {
        app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
            string.Join("\n", endpointSources.SelectMany(source => source.Endpoints)));
    }
    
    app.Run();
    

    That interface allows retrieving more than just the route as the next example shows :

    app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
    {
        var sb = new StringBuilder();
        var endpoints = endpointSources.SelectMany(es => es.Endpoints);
        foreach (var endpoint in endpoints)
        {
            if(endpoint is RouteEndpoint routeEndpoint)
            {
                _ = routeEndpoint.RoutePattern.RawText;
                _ = routeEndpoint.RoutePattern.PathSegments;
                _ = routeEndpoint.RoutePattern.Parameters;
                _ = routeEndpoint.RoutePattern.InboundPrecedence;
                _ = routeEndpoint.RoutePattern.OutboundPrecedence;
            }
    
            var routeNameMetadata = endpoint.Metadata.OfType<Microsoft.AspNetCore.Routing.RouteNameMetadata>().FirstOrDefault();
            _ = routeNameMetadata?.RouteName;
    
            var httpMethodsMetadata = endpoint.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault();
            _ = httpMethodsMetadata?.HttpMethods; // [GET, POST, ...]
    
            // There are many more metadata types available...
    });
    

    This is actually mentioned in the Endpoint Definition section of the Routing docs which ... I've never bothered to read before :

    An ASP.NET Core endpoint is:

    • Executable: Has a RequestDelegate.
    • Extensible: Has a Metadata collection.
    • Selectable: Optionally, has routing information.
    • Enumerable: The collection of endpoints can be listed by retrieving the EndpointDataSource from DI.