在AspNetCore中,给定一个FilterContext,我希望得到一个路由模板,例如
{controller}/{action}/{id?}
在Microsoft.AspNet.WebApi中,我可以从以下位置获取路由模板:
HttpControllerContext.RouteData.Route.RouteTemplate
在System.Web.Mvc中,我可以从以下网址获取:
ControllerContext.RouteData.Route as RouteBase
在AspNetCore中有:
FilterContext.ActionDescriptor.AttributeRouteInfo.Template
但是,并非所有路线都是属性路线。
如果属性不可用,则根据检查,可以从以下位置组装默认路由和/或映射路由:
FilterContext.RouteData.Routers.OfType<Microsoft.AspNetCore.Routing.RouteBase>().First()
但我正在寻找有记录或更好的方法。
答案 0 :(得分:3)
更新(2021 年 1 月 24 日)
有一种更简单的方法可以直接通过 RoutePattern
检索 HttpContext
。
FilterContext filterContext;
var endpoint = filterContext.HttpContext.GetEndpoint() as RouteEndpoint;
var template = endpoint?.RoutePattern?.RawText;
if (template is null)
throw new Exception("No route template found, that's absurd");
Console.WriteLine(template);
GetEndpoint()
是 EndpointHttpContextExtensions
命名空间内的 Microsoft.AspNetCore.Http
类中提供的扩展方法
一个 ASP.NET Core 应用程序(至少对于 3.1)的所有路由构建器都是通过 IEndpointRouteBuilder
公开和注册的,但不幸的是,这没有在 DI 容器中注册,因此您无法获取它直接。我看到这个接口暴露的唯一地方是在中间件中。
因此,您可以使用这些中间件之一构建集合或字典,然后将其用于您的目的。
例如
用于构建端点集合/字典的扩展类
internal static class IEndpointRouteBuilderExtensions
{
internal static void BuildMap(this IEndpointRouteBuilder endpoints)
{
foreach (var item in endpoints.DataSources)
foreach (RouteEndpoint endpoint in item.Endpoints)
{
/* This is needed for controllers with overloaded actions
* Use the RoutePattern.Parameters here
* to generate a unique display name for the route
* instead of this list hack
*/
if (Program.RouteTemplateMap.TryGetValue(endpoint.DisplayName, out var overloadedRoutes))
overloadedRoutes.Add(endpoint.RoutePattern.RawText);
else
Program.RouteTemplateMap.Add(endpoint.DisplayName, new List<string>() { endpoint.RoutePattern.RawText });
}
}
}
public class Program
{
internal static readonly Dictionary<string, List<string>> RouteTemplateMap = new Dictionary<string, List<string>>();
/* Rest of things */
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
/* all other middlewares */
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
//Use this at the last middlware exposing IEndpointRouteBuilder so that all the routes are built by this point
endpoints.BuildMap();
});
}
然后您可以使用该字典或集合,从 FilterContext 中检索路由模板。
FilterContext filterContext;
Program.RouteTemplateMap.TryGetValue(filterContext.ActionDescriptor.DisplayName, out var template);
if (template is null)
throw new Exception("No route template found, that's absurd");
/* Use the ActionDescriptor.Parameters here
* to figure out which overloaded action was called exactly */
Console.WriteLine(string.Join('\n', template));
为了解决重载动作的情况,路由模板使用了一个字符串列表(而不仅仅是字典中的一个字符串)
您可以将 ActionDescriptor.Parameters
与 RoutePattern.Parameters
结合使用来为该路线生成唯一的显示名称。
答案 1 :(得分:0)
这是组装版本,但仍在寻找更好的答案。
FilterContext context;
string routeTemplate = context.ActionDescriptor.AttributeRouteInfo?.Template;
if (routeTemplate == null)
{
// manually mapped routes or default routes
// todo is there a better way, not 100% sure that this is correct either
// https://github.com/aspnet/Routing/blob/1b0258ab8fccff1306e350fd036d05c3110bbc8e/src/Microsoft.AspNetCore.Routing/Template/TemplatePart.cs
IEnumerable<string> segments = context.RouteData.Routers.OfType<Microsoft.AspNetCore.Routing.RouteBase>()
.FirstOrDefault()?.ParsedTemplate.Segments.Select(s => string.Join(string.Empty, s.Parts
.Select(p => p.IsParameter ? $"{{{(p.IsCatchAll ? "*" : string.Empty)}{p.Name}{(p.IsOptional ? "?" : string.Empty)}}}" : p.Text)));
if (segments != null)
{
routeTemplate = string.Join("/", segments);
}
}