Dan*_*ach 5 asp.net-core-mvc asp.net-core asp.net-core-routing
在 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()
但我正在寻找记录的或简单的更好的方法。
更新(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);
Run Code Online (Sandbox Code Playgroud)
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 */
}
Run Code Online (Sandbox Code Playgroud)
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();
});
}
Run Code Online (Sandbox Code Playgroud)
然后您可以使用该字典或集合从 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));
Run Code Online (Sandbox Code Playgroud)
为了解决重载操作的情况,将字符串列表用于路由模板(而不仅仅是字典中的字符串)。
您可以ActionDescriptor.Parameters结合RoutePattern.Parameters使用 来为该路由生成唯一的显示名称。
这些是组装版本,但仍在寻找更好的答案。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
RoutePattern routePattern = null;
var endpointFeature = context.HttpContext.Features[typeof(Microsoft.AspNetCore.Http.Features.IEndpointFeature)]
as Microsoft.AspNetCore.Http.Features.IEndpointFeature;
var endpoint = endpointFeature?.Endpoint;
if (endpoint != null)
{
routePattern = (endpoint as RouteEndpoint)?.RoutePattern;
}
string formatRoutePart(RoutePatternPart part)
{
if (part.IsParameter)
{
RoutePatternParameterPart p = (RoutePatternParameterPart)part;
return $"{{{(p.IsCatchAll ? "*" : string.Empty)}{p.Name}{(p.IsSeparator ? " ? " : string.Empty)}}}";
}
else if (part.IsLiteral)
{
RoutePatternLiteralPart p = (RoutePatternLiteralPart)part;
return p.Content;
}
else if(part.IsSeparator)
{
RoutePatternSeparatorPart p = (RoutePatternSeparatorPart)part;
return p.Content;
}
else
{
throw new NotSupportedException("Unknown Route PatterPart");
}
}
if (routePattern != null)
{
// https://github.com/aspnet/Routing/blob/1b0258ab8fccff1306e350fd036d05c3110bbc8e/src/Microsoft.AspNetCore.Routing/Template/TemplatePart.cs
routeString = string.Join("/", routePattern.PathSegments.SelectMany(s => s.Parts).Select(p => formatRoutePart(p)));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2099 次 |
| 最近记录: |