【问题标题】:Can I get a the RouteTemplate from AspNetCore FilterContext?我可以从 AspNetCore FilterContext 获取 RouteTemplate 吗?
【发布时间】:2017-11-30 15:14:41
【问题描述】:

在 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() 但我正在寻找一种记录在案的或更好的方法。

【问题讨论】:

    标签: asp.net-core asp.net-core-mvc asp.net-core-routing


    【解决方案1】:

    更新(2021 年 1 月 24 日)
    有一种更简单的方法可以直接通过HttpContext 检索RoutePattern。

        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 容器中注册,因此您无法直接获取它。我看到这个接口暴露的唯一地方是中间件。 因此,您可以使用其中一个中间件构建集合或字典,然后将其用于您的目的。
    例如

    程序.cs

    扩展类来构建你的端点集合/字典

        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 */
        }
    

    Startup.cs

            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 结合使用,为该路线生成唯一的显示名称。

    【讨论】:

    • 再看一眼,查看更新的答案(24-Jan-21),它看起来与 OP 自己的答案相似。 @Daniel,您能否详细说明为什么这对您不起作用?
    【解决方案2】:

    这些是组装版本,但仍在寻找更好的答案。

    AspNetCore 2.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);
            }
        }
    

    带有端点路由的 AspNetCore 3.0

                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)));
                }
    

    【讨论】:

      猜你喜欢
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 2021-04-05
      • 2016-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多