如何遍历所有路线? [英] How can I loop through all the routes?

查看:71
本文介绍了如何遍历所有路线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从一个mvc(2)用户控件内部,我想遍历所有路由值.

From inside a mvc (2) user control, I want to loop through all the route values.

所以,如果我有类似的控制器:

So if I have controllers like:

UserController
AccountController

我需要一个将出现在url中的值的集合,例如:

I need a collection of the values that will appear in the url like:

/user/...
/account/...

即值用户,帐户.

我怎么能得到这个?

我尝试过RouteTables,但无法弄清楚.

I tried RouteTables but couldn't figure it out.

推荐答案

哦,这是一个让我忙一个小时的好问题. 为了实现所需的功能,我们需要了解MVC源代码和一点点反思.

Oh, really a good question to keep my self busy for an hour. To achieve the required functionality , we need to hook into MVC source and little bit of reflections.

  1. 默认情况下,路由名称不可用,因此我们需要编写一个路由集合扩展,以将路由名称保存在RouteData令牌中.

  1. By default Route names are not available , so we need to write a Route collection extension to save Route Name in RouteData tokens.

public static Route MapRouteWithName(this RouteCollection routes,string name, string   url, object defaults=null, object constraints=null)
{

Route route = routes.MapRoute(name, url, defaults, constraints);
route.DataTokens = new RouteValueDictionary();
route.DataTokens.Add("RouteName", name);
return route;
}

  • 修改global.asax maproute调用以调用先前的扩展

  • Modify the global.asax maproute call to invoke previous extension

    routes.MapRouteWithName(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
    

  • 对MVC PathHelper进行了一些修改.(在项目中包括此帮助器)

  • Modified the MVC PathHelper a little bit.(Include this helper in the project)

    using System;
    using System.Collections.Specialized;
    using System.Web;
    
    public static class PathHelpers
    {
    
    // this method can accept an app-relative path or an absolute path for contentPath
    public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath)
    {
        if (String.IsNullOrEmpty(contentPath))
        {
            return contentPath;
        }
    
        // many of the methods we call internally can't handle query strings properly, so just strip it out for
        // the time being
        string query;
        contentPath = StripQuery(contentPath, out query);
    
        return GenerateClientUrlInternal(httpContext, contentPath) + query;
    }
    
    private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
    {
        if (String.IsNullOrEmpty(contentPath))
        {
            return contentPath;
        }
    
        // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
        bool isAppRelative = contentPath[0] == '~';
        if (isAppRelative)
        {
            string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
            string modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
            return GenerateClientUrlInternal(httpContext, modifiedAbsoluteContentPath);
        }
    
        string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
        string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
        return absoluteUrlToDestination;
    }
    
    public static string MakeAbsolute(string basePath, string relativePath)
    {
        // The Combine() method can't handle query strings on the base path, so we trim it off.
        string query;
        basePath = StripQuery(basePath, out query);
        return VirtualPathUtility.Combine(basePath, relativePath);
    }
    
    public static string MakeRelative(string fromPath, string toPath)
    {
        string relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath);
        if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?')
        {
            // Sometimes VirtualPathUtility.MakeRelative() will return an empty string when it meant to return '.',
            // but links to {empty string} are browser dependent. We replace it with an explicit path to force
            // consistency across browsers.
            relativeUrl = "./" + relativeUrl;
        }
        return relativeUrl;
    }
    
    private static string StripQuery(string path, out string query)
    {
        int queryIndex = path.IndexOf('?');
        if (queryIndex >= 0)
        {
            query = path.Substring(queryIndex);
            return path.Substring(0, queryIndex);
        }
        else
        {
            query = null;
            return path;
        }
    }
    
    }
    

  • 在控制器中添加一些辅助方法

  • Add few Helper methods in controller

    public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteCollection routeCollection, RequestContext requestContext)
    {
    
        RouteValueDictionary mergedRouteValues = MergeRouteValues(actionName, controllerName);
    
        VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
        if (vpd == null)
        {
            return null;
        }
    
        string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
        return modifiedUrl;
    }
    public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName)
    {
        // Create a new dictionary containing implicit and auto-generated values
        RouteValueDictionary mergedRouteValues = new RouteValueDictionary();
    
        // Merge explicit parameters when not null
        if (actionName != null)
        {
            mergedRouteValues["action"] = actionName;
        }
    
        if (controllerName != null)
        {
            mergedRouteValues["controller"] = controllerName;
        }
    
        return mergedRouteValues;
    }
    

  • 现在,我们可以编写一些反射逻辑来读取控制器,操作和路由名称.

  • Now we can write some reflection logics to read controllers, actions and routenames.

    Dictionary<string, List<string>> controllersAndActions = new Dictionary<string, List<string>>();
    
    // Get all the controllers
    var controllers = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t));
    
    foreach (var controller in controllers)
    {
        List<string> actions = new List<string>();
        //Get all methods without HttpPost and with return type action result
        var methods = controller.GetMethods().Where(m => typeof(ActionResult).IsAssignableFrom(m.ReturnType)).Where(a=>!a.GetCustomAttributes(typeof(HttpPostAttribute),true).Any());
        methods.ToList().ForEach(a => {
            actions.Add(a.Name);
        });
        var controllerName = controller.Name;
        if (controllerName.EndsWith("Controller"))
        {
            var nameLength = controllerName.Length - "Controller".Length;
            controllerName = controllerName.Substring(0, nameLength);
        }
        controllersAndActions.Add(controllerName, actions);
    }
    List<string> allowedRoutes = new List<string>();
    
    var routeNames = RouteTable.Routes.Where(o=>o.GetRouteData(this.HttpContext)!=null).Select(r=>r.GetRouteData(this.HttpContext).DataTokens["RouteName"].ToString());
    foreach (var cName in controllersAndActions)
    {
        foreach (var aName in cName.Value)
        {
            foreach (var item in routeNames)
            {
                allowedRoutes.Add(GenerateUrl(item, aName, cName.Key, RouteTable.Routes, this.Request.RequestContext));
            }
        }
    
    }
    

  • 要记住的要点:如果在路由中定义了任何默认参数,则这些控制器和操作的url将为空.例如在上面的示例中,"/Home/Index"将显示为"/"

  • Points to remember :If in the route you have defined any default parameters, then url for those controller and action will be empty. e.g. in above example "/Home/Index" will be shown as "/"

    下载示例应用程序下载链接

    这篇关于如何遍历所有路线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆