Daily Knowledge Drop
ASP.NET Core has a build in class, EndpointDataSource
, which contains information about a route, while the IEnumerable<EndpointDataSource>
collection, available through dependency injection contains information about all endpoints of an application.
Information about a specific endpoint can also be retrieved from the HttpContext
for a specific request.
Listing routes
First, lets look at getting all the routes (endpoints) for an application - this can be done by injecting IEnumerable<EndpointDataSource>
into the relevant constructor (or endpoint delegate in the below example):
app.MapGet("/routes", (IEnumerable<EndpointDataSource> routes) =>
string.Join(Environment.NewLine, routes.SelectMany(es => es.Endpoints)));
Browsing to the /routes
endpoint, the following is returned (the 4 endpoints which make up the sample api):
HTTP: GET /routes
HTTP: GET /user/{userId}
HTTP: GET /routewithid/{id}
HTTP: GET /routes/info
Additional route data
Additional route metadata can also be retrieved from EndpointDataSource
. The below /routes/info
endpoint extends on the above basic endpoint adding additional data for each endpoint:
app.MapGet("/routes/info", (IEnumerable<EndpointDataSource> endpointSources) =>
{
var sb = new StringBuilder();
var endpoints = endpointSources.SelectMany(es => es.Endpoints);
// iterate through each endpoint
foreach (var endpoint in endpoints)
{
// get the display name
sb.AppendLine($"Endpoint: {endpoint.DisplayName}");
// check if the endpoint is a RouteEndpoint
if (endpoint is RouteEndpoint routeEndpoint)
{
// output route pattern information
sb.AppendLine($" - Segment Count: " +
$"{routeEndpoint.RoutePattern.PathSegments.Count}");
sb.AppendLine($" - Parameters: ");
foreach(var param in routeEndpoint.RoutePattern.Parameters)
{
sb.AppendLine($" - {param.Name}");
}
sb.AppendLine($" - Inbound Precedence: " +
$"{routeEndpoint.RoutePattern.InboundPrecedence}");
sb.AppendLine($" - Outbound Precedence: " +
$"{routeEndpoint.RoutePattern.OutboundPrecedence}");
}
// output meta data
sb.AppendLine($" - Meta Count: {endpoint.Metadata.Count()}");
foreach (var meta in endpoint.Metadata)
{
sb.AppendLine($" - Meta Type: {meta}");
}
}
return sb.ToString();
});
Browsing to the /routes/info
endpoint, one can see the additional information:
Endpoint: HTTP: GET /routes
- Segment Count: 1
- Parameters:
- Inbound Precedence: 1
- Outbound Precedence: 5
- Meta Count: 2
- Meta Type: System.String <<Main>$>b__0_0(System.Collections.Generic.IEnumerable`1[Microsoft.AspNetCore.Routing.EndpointDataSource])
- Meta Type: Microsoft.AspNetCore.Routing.HttpMethodMetadata
Endpoint: HTTP: GET /user/{userId}
- Segment Count: 2
- Parameters:
- userId
- Inbound Precedence: 1,3
- Outbound Precedence: 5,3
- Meta Count: 2
- Meta Type: System.String <<Main>$>b__0_1(System.String)
- Meta Type: Microsoft.AspNetCore.Routing.HttpMethodMetadata
Endpoint: HTTP: GET /routewithid/{id}
- Segment Count: 2
- Parameters:
- id
- Inbound Precedence: 1,3
- Outbound Precedence: 5,3
- Meta Count: 3
- Meta Type: System.String <<Main>$>b__0_2(Microsoft.AspNetCore.Http.HttpContext, System.String)
- Meta Type: System.Runtime.CompilerServices.NullableContextAttribute
- Meta Type: Microsoft.AspNetCore.Routing.HttpMethodMetadata
Endpoint: HTTP: GET /routes/info
- Segment Count: 2
- Parameters:
- Inbound Precedence: 1,1
- Outbound Precedence: 5,5
- Meta Count: 2
- Meta Type: System.String <<Main>$>b__0_3(System.Collections.Generic.IEnumerable`1[Microsoft.AspNetCore.Routing.EndpointDataSource])
- Meta Type: Microsoft.AspNetCore.Routing.HttpMethodMetadata
Some examples of the type of information available:
- The count and type of each segment making up the endpoint url (e.g. lines 2 and 10)
- The list of parameters for an endpoint (e.g. line 12)
- The lambda delegate for each endpoint (e.g lines 7 and 16)
The metadata information would need to be checked and cast to the specific type to retrieve additional information - but additional information is available.
HttpContext route information
Information can also be received from the HttpContext
of a specific request, for the endpoint being called. The information is the same as is contained in the EndpointDataSource.Endpoints
collection used in the above examples:
app.MapGet("/routewithid/{id}", (HttpContext context, string id) =>
$"Route `{context.GetEndpoint()?.DisplayName}` with id = '{id}'");
Calling this endpoint with an id of "sampleId" (/routewithid/sampleId
) results in the following output:
Route `HTTP: GET /routewithid/{id}` with id = 'sampleid'
Notes
There are a number of useful outputs which could be done using this metadata information, such as outputting the information to be consumed into external api documentation, or adding an endpoint graph to the application.
References
How to list all routes in an ASP.NET Core application
Routing in ASP.NET Core
Daily Drop 86: 01-06-2022
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.