我怎样才能遍历所有路线?

时间:2012-02-10 16:57:17

标签: c# asp.net asp.net-mvc

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

所以,如果我有控制器,如:

UserController
AccountController

我需要一组将显示在网址中的值,如:

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

即。值user,account。

我怎么能得到这个?

我尝试过RouteTables,但无法理解。

1 个答案:

答案 0 :(得分:15)

哦,真的是一个很好的问题,让自己忙一个小时。 为了实现所需的功能,我们需要关注MVC源代码和一些反思。

  1. 默认路由名称不可用,因此我们需要编写Route收集扩展名以在RouteData令牌中保存路由名称。

    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;
    }
    
  2. 修改global.asax maproute调用以调用以前的扩展名

    routes.MapRouteWithName(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
    
  3. 稍微修改了MVC PathHelper。(在项目中包含这个帮助器)

    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;
        }
    }
    
    }
    
  4. 在控制器中添加几个Helper方法

    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;
    }
    
  5. 现在我们可以编写一些反射逻辑来读取控制器,动作和路径名。

    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));
            }
        }
    
    }
    
  6. 要记住的要点:如果您在路线中定义了任何默认参数,那么这些控制器和操作的网址将为空。例如在上面的例子&#34; / Home / Index&#34;将显示为&#34; /&#34;

  7. 下载示例应用Link To Download

    List item