为什么在asp.net mvc中的常用路由之前首先映射特殊路由?

时间:2016-02-26 20:24:46

标签: asp.net-mvc asp.net-mvc-5 url-routing asp.net-mvc-routing asp.net-mvc-5.2

来自www:

  

...路由引擎将采用与提供的URL匹配的第一条路由,并尝试使用该路由中的路由值。因此,应该首先在表格中添加不太常见或更专业的路线,而稍后应该添加更一般的路线......

为什么我要首先映射专门的路线?有人可以给我一个例子,我可以看到"首先映射公共路线" ?

1 个答案:

答案 0 :(得分:17)

  

路由引擎将采用与提供的URL匹配的第一条路由,并尝试使用该路由中的路由值。

发生这种情况的原因是因为RouteTable被用作switch-case语句。请注意以下内容:

int caseSwitch = 1;
switch (caseSwitch)
{
    case 1:
        Console.WriteLine("Case 1");
        break;
    case 1:
        Console.WriteLine("Second Case 1");
        break;
    default:
        Console.WriteLine("Default case");
        break;
}

如果caseSwitch1,则永远不会到达第二个块,因为第一个块会捕获它。

Route类遵循类似的模式(在GetRouteDataGetVirtualPath方法中)。他们可以返回2个州:

  1. 一组路由值(或VirtualPath)中的GetVirtualPath对象。这表示路由与请求匹配。
  2. null。这表示路由与请求不匹配。
  3. 在第一种情况下,MVC使用路由生成的路由值来查找Action方法。在这种情况下,RouteTable不再进行分析。

    在第二种情况下,MVC将检查Route中的下一个RouteTable以查看它是否与请求匹配(内置行为与URL和约束匹配,但从技术上讲,您可以匹配任何内容在HTTP请求中)。再一次,该路由可以返回一组RouteValuesnull,具体取决于结果。

    如果您尝试使用上面的switch-case语句,程序将无法编译。但是,如果您配置从不返回null或在更多情况下返回RouteValues对象的路径,程序将编译,但会出错。

    配置错误示例

    以下是我经常在StackOverflow(或其中的一些变体)上发布的经典示例:

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
            routes.MapRoute(
                name: "CustomRoute",
                url: "{segment1}/{action}/{id}",
                defaults: new { controller = "MyController", action = "Index", id = UrlParameter.Optional }
            );
    
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
    

    在这个例子中:

    1. CustomRoute将匹配长度为1,2或3段的任何网址(请注意,segment1是必需的,因为它没有默认值。)
    2. Default将匹配任何长度为0,1,2或3段的网址。
    3. 因此,如果应用程序通过了网址\Home\AboutCustomRoute将匹配,并将以下RouteValues提供给MVC:

      1. segment1 = "Home"
      2. controller = "MyController"
      3. action = "About"
      4. id = {}
      5. 这将使MVC在名为About的控制器上查找名为MyControllerController的操作,如果它不存在则会失败。在这种情况下,Default路由是无法访问的执行路径,因为即使它匹配2段URL,框架也不会给它机会,因为第一场比赛获胜。

        修复配置

        有关如何继续修复配置的几个选项。但所有这些都取决于第一场比赛获胜的行为,然后路由不会再看了。

        选项1:添加一个或多个文字段

        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
                routes.MapRoute(
                    name: "CustomRoute",
                    url: "Custom/{action}/{id}",
        
                    // Note, leaving `action` and `id` out of the defaults
                    // makes them required, so the URL will only match if 3
                    // segments are supplied begining with Custom or custom.
                    // Example: Custom/Details/343
                    defaults: new { controller = "MyController" }
                );
        
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
        

        选项2:添加一个或多个RegEx约束

        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
                routes.MapRoute(
                    name: "CustomRoute",
                    url: "{segment1}/{action}/{id}",
                    defaults: new { controller = "MyController", action = "Index", id = UrlParameter.Optional },
                    constraints: new { segment1 = @"house|car|bus" }
                );
        
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
        

        选项3:添加一个或多个自定义约束

        public class CorrectDateConstraint : IRouteConstraint
        {
            public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
            {
                var year = values["year"] as string;
                var month = values["month"] as string;
                var day = values["day"] as string;
        
                DateTime theDate;
                return DateTime.TryParse(year + "-" + month + "-" + day, System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.None, out theDate);
            }
        }
        
        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
                routes.MapRoute(
                    name: "CustomRoute",
                    url: "{year}/{month}/{day}/{article}",
                    defaults: new { controller = "News", action = "ArticleDetails" },
                    constraints: new { year = new CorrectDateConstraint() }
                );
        
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
        

        选项4:使必需细分+使细分数量与现有路线不匹配

        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
                routes.MapRoute(
                    name: "CustomRoute",
                    url: "{segment1}/{segment2}/{action}/{id}",
                    defaults: new { controller = "MyController" }
                );
        
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
        

        在上述情况下,CustomRoute只会匹配一个包含4个段的网址(请注意这些可以是任何值)。与以前一样,Default路由仅匹配具有0,1,2或3个段的URL。因此,没有无法到达的执行路径。

        选项5:为自定义行为实施RouteBase(或路由)

        路由不支持开箱即用(例如在特定域或子域上匹配)的任何内容都可以由implementing your own RouteBase subclass或Route子类完成。这也是了解路由如何/为何如此工作的最佳方式。

        public class SubdomainRoute : Route
        {
            public SubdomainRoute(string url) : base(url, new MvcRouteHandler()) {}
        
            public override RouteData GetRouteData(HttpContextBase httpContext)
            {
                var routeData = base.GetRouteData(httpContext);
                if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place.
                string subdomain = httpContext.Request.Params["subdomain"]; // A subdomain specified as a query parameter takes precedence over the hostname.
                if (subdomain == null) {
                    string host = httpContext.Request.Headers["Host"];
                    int index = host.IndexOf('.');
                    if (index >= 0)
                        subdomain = host.Substring(0, index);
                }
                if (subdomain != null)
                    routeData.Values["subdomain"] = subdomain;
                return routeData;
            }
        
            public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
            {
                object subdomainParam = requestContext.HttpContext.Request.Params["subdomain"];
                if (subdomainParam != null)
                    values["subdomain"] = subdomainParam;
                return base.GetVirtualPath(requestContext, values);
            }
        }
        

        这门课程来自:Is it possible to make an ASP.NET MVC route based on a subdomain?

        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        
                routes.Add(new SubdomainRoute(url: "somewhere/unique"));
        
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
        
          

        注意:这里真正的问题是,大多数人都认为他们的路线应该都像Default路线。复制,粘贴,完成,对吧?错。

             

        这种方法通常会出现两个问题:

             
            
        1. 几乎所有其他路线都应该至少有一个文字段(如果你是这样的话,还是一个约束)。
        2.   
        3. 最符合逻辑的行为通常是让其余路线具有必需的段。
        4.         

          另一个常见的误解是,可选细分意味着你可以忽略任何细分,但实际上你只能忽略最右边的一个或多个细分。

               

          Microsoft成功地使路由基于约定,可扩展且功能强大。他们没有让它直观易懂。实际上每个人都在第一次尝试时失败了(我知道我做过了!)。幸运的是,一旦你了解它是如何工作的,它就不是很困难了。