Подтвердить что ты не робот

Как я могу пройти через все маршруты?

Изнутри пользовательского элемента управления mvc (2) я хочу пройти все значения маршрута.

Итак, если у меня есть контроллеры вроде:

UserController
AccountController

Мне нужен набор значений, которые появятся в URL-адресе, например:

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

то есть. значения user, account.

Как я могу получить это?

Я попробовал RouteTables, но не мог понять.

4b9b3361

Ответ 1

О, действительно хороший вопрос, чтобы я не мог заняться в течение часа. Чтобы достичь требуемой функциональности, нам нужно подключить источник MVC и немного отражений.

  • По умолчанию имена маршрутов недоступны, поэтому нам нужно написать расширение коллекции маршрутов для сохранения имени маршрута в токенах 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;
    }
    
  • Измените вызов maproute global.asax для вызова предыдущего расширения

    routes.MapRouteWithName(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );
    
  • Немного изменил 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;
        }
    }
    
    }
    
  • Добавить несколько вспомогательных методов в контроллере

    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;
    }
    
  • Теперь мы можем написать некоторые логики отражения, чтобы читать контроллеры, действия и названия программ.

    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" будет отображаться как "/"

  • Загрузите пример приложения Ссылка на скачивание

    List item