How to get a list of all routes in ASP.NET Core?

30,909

Solution 1

To get at all the routes, you need to use the ApiExplorer part of MVC. You can either mark all your actions with an attribute or use a convention like this one:

public class ApiExplorerVisibilityEnabledConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            if (controller.ApiExplorer.IsVisible == null)
            {
                controller.ApiExplorer.IsVisible = true;
                controller.ApiExplorer.GroupName = controller.ControllerName;
            }
        }
    }
}

In Startup.cs, add your new in ConfigureServices(...)

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(
        options => 
        {
            options.Conventions.Add(new ApiExplorerVisibilityEnabledConvention());
            options.
        }
}

In your ActionFilter you can then use constructor injection to get the ApiExplorer:

public class MyFilter : ActionFilterAttribute
{      
    private readonly IApiDescriptionGroupCollectionProvider descriptionProvider;

    public MyFilter(IApiDescriptionGroupCollectionProvider descriptionProvider) 
    {
        this.descriptionProvider = descriptionProvider;
    }

    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
        base.OnActionExecuting(actionContext);

        // The convention groups all actions for a controller into a description group
        var actionGroups = descriptionProvider.ApiDescriptionGroups.Items;

        // All the actions in the controller are given by
        var apiDescription = actionGroup.First().Items.First();

        // A route template for this action is
        var routeTemplate = apiDescription.RelativePath
    }
}

ApiDescription, which has the RelativePath, which is the route template for that route:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Microsoft.AspNetCore.Mvc.ApiExplorer
{
    public class ApiDescription
    {
        public string GroupName { get; set; }
        public string HttpMethod { get; set; }
        public IList<ApiParameterDescription> ParameterDescriptions { get; } = new List<ApiParameterDescription>();
        public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
        public string RelativePath { get; set; }
        public ModelMetadata ResponseModelMetadata { get; set; }
        public Type ResponseType { get; set; }
        public IList<ApiRequestFormat> SupportedRequestFormats { get; } = new List<ApiRequestFormat>();
        public IList<ApiResponseFormat> SupportedResponseFormats { get; } = new List<ApiResponseFormat>();
    }
}

Solution 2

If you're on ASP.NET Core 3.0+, that means you're using endpoint routing, then you can list all routes with EndpointDataSources.

Inject IEnumerable<EndpointDataSource> to your controller/endpoint then extract anything you need. It works with both controller actions, endpoints, and partially with razor pages (razor pages don't seem to expose available HTTP methods).

[Route("/-/{controller}")]
public class InfoController : Controller
{
    private readonly IEnumerable<EndpointDataSource> _endpointSources;

    public InfoController(
        IEnumerable<EndpointDataSource> endpointSources
    )
    {
        _endpointSources = endpointSources;
    }

    [HttpGet("endpoints")]
    public async Task<ActionResult> ListAllEndpoints()
    {
        var endpoints = _endpointSources
            .SelectMany(es => es.Endpoints)
            .OfType<RouteEndpoint>();
        var output = endpoints.Select(
            e =>
            {
                var controller = e.Metadata
                    .OfType<ControllerActionDescriptor>()
                    .FirstOrDefault();
                var action = controller != null
                    ? $"{controller.ControllerName}.{controller.ActionName}"
                    : null;
                var controllerMethod = controller != null
                    ? $"{controller.ControllerTypeInfo.FullName}:{controller.MethodInfo.Name}"
                    : null;
                return new
                {
                    Method = e.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault()?.HttpMethods?[0],
                    Route = $"/{e.RoutePattern.RawText.TrimStart('/')}",
                    Action = action,
                    ControllerMethod = controllerMethod
                };
            }
        );
        
        return Json(output);
    }
}

when you visit /-/info/endpoints, you'll get a list of routes as JSON:

[
  {
    "method": "GET",
    "route": "/-/info/endpoints", // <-- controller action
    "action": "Info.ListAllEndpoints",
    "controllerMethod": "Playground.Controllers.InfoController:ListAllEndpoints"
  },
  {
    "method": "GET",
    "route": "/WeatherForecast", // <-- controller action
    "action": "WeatherForecast.Get",
    "controllerMethod": "Playground.Controllers.WeatherForecastController:Get"
  },
  {
    "method": "GET",
    "route": "/hello", // <-- endpoint route
    "action": null,
    "controllerMethod": null
  },
  {
    "method": null,
    "route": "/about", // <-- razor page
    "action": null,
    "controllerMethod": null
  },
]

Solution 3

You can take a look at this awesome GitHub project:

https://github.com/kobake/AspNetCore.RouteAnalyzer

Readme from the project

=======================

AspNetCore.RouteAnalyzer

View all route information for ASP.NET Core project.

Pickuped screenshot

screenshot

Usage on your ASP.NET Core project

Install NuGet package

PM> Install-Package AspNetCore.RouteAnalyzer

Edit Startup.cs

Insert code services.AddRouteAnalyzer(); and required using directive into Startup.cs as follows.

using AspNetCore.RouteAnalyzer; // Add

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddRouteAnalyzer(); // Add
}

Case1: View route information on browser

Insert code routes.MapRouteAnalyzer("/routes"); into Startup.cs as follows.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ....
    app.UseMvc(routes =>
    {
        routes.MapRouteAnalyzer("/routes"); // Add
        routes.MapRoute(
            name: "default",
            template: "{controller}/{action=Index}/{id?}");
    });
}

Then you can access url of http://..../routes to view all route informations on your browser. (This url /routes can be customized by MapRouteAnalyzer().)

screenshot

Case2: Print routes on VS output panel

Insert a code block as below into Startup.cs.

public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    IApplicationLifetime applicationLifetime, // Add
    IRouteAnalyzer routeAnalyzer // Add
)
{
    ...

    // Add this block
    applicationLifetime.ApplicationStarted.Register(() =>
    {
        var infos = routeAnalyzer.GetAllRouteInformations();
        Debug.WriteLine("======== ALL ROUTE INFORMATION ========");
        foreach (var info in infos)
        {
            Debug.WriteLine(info.ToString());
        }
        Debug.WriteLine("");
        Debug.WriteLine("");
    });
}

Then you can view all route informations on VS output panel.

screenshot

Solution 4

Not been successful with the above, as I wanted a full url which I didn't have to mess around with things to construct the url, but instead let the framework handle the resolution. So following on from AspNetCore.RouteAnalyzer and countless googling and searching, I didn't find a definitive answer.

The following works for me for typical home controller and area controller:

public class RouteInfoController : Controller
{
    // for accessing conventional routes...
    private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;

    public RouteInfoController(
        IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
    {
        _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
    }

    public IActionResult Index()
    {
        StringBuilder sb = new StringBuilder();

        foreach (ActionDescriptor ad in _actionDescriptorCollectionProvider.ActionDescriptors.Items)
        {
            var action = Url.Action(new UrlActionContext()
            {
                Action = ad.RouteValues["action"],
                Controller = ad.RouteValues["controller"],
                Values = ad.RouteValues
            });

            sb.AppendLine(action).AppendLine().AppendLine();
        }

        return Ok(sb.ToString());
    }

This will output the following in my simple solution:

/
/Home/Error
/RouteInfo
/RouteInfo/Links
/Area51/SecureArea

The above was done using dotnetcore 3 preview but I think it should work with dotnetcore 2.2. Additionally getting the url this way will take into consideration any conventions that have been put in place including the excellent slugify as brought to light on Scott Hanselman's Blog

Solution 5

You can get an HttpRouteCollection from the HttpActionContext via:

actionContext.RequestContext.Configuration.Routes

RequestContext

HttpConfiguration

HttpRouteCollection

-- After Question Updated --

The ActionExecutingContext has a RouteData property that it inherits from ControllerContext, which exposes the DataTokens property (which is a route value dictionary). It is probably not the same collection you're used to working with, but it does provide access to that collection:

actionContext.RouteData.DataTokens

DataTokens

Share:
30,909
clhereistian
Author by

clhereistian

Updated on January 16, 2022

Comments

  • clhereistian
    clhereistian over 2 years

    In ASP.NET Core, is there a way to see a list of all the routes defined in Startup? We are using the MapRoute extension method of IRouteBuilder to define the routes.

    We are migrating an older project WebAPI project. There we could use GlobalConfiguration.Configuration.Routes to get all the routes.

    More specifically, we are doing this within an action filter.

    public class MyFilter : ActionFilterAttribute
    {      
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            base.OnActionExecuting(actionContext);
    
            // This no longer works
            // var allRoutes = GlobalConfiguration.Configuration.Routes;
    
            // var allRoutes = ???
        }
    }