为什么要特别路线在asp.net mvc的共同路线之前首先映射 [英] Why map special routes first before common routes in asp.net mvc

查看:88
本文介绍了为什么要特别路线在asp.net mvc的共同路线之前首先映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从WWW:...路由引擎将所提供的URL匹配的第一路径,并尝试在该路线使用路由值。因此,较少见的或更专门的路线应首先被添加到该表。 ,而更普遍的路线应该在以后添加...

为什么要我先映射专业化路线?有人可以给我一个例子,请在那里我可以看到首先映射常见的途径的失败?

谢谢!


解决方案

  

路由引擎会提供的URL匹配的第一条路线,并试图在该路由使用路由值。


为什么出现这种情况的原因是因为 RouteTable 用于像一个开关case语句。图片如下:

  INT caseSwitch = 1;
开关(caseSwitch)
{
    情况1:
        Console.WriteLine(案例1);
        打破;
    情况1:
        Console.WriteLine(第二个案例1);
        打破;
    默认:
        Console.WriteLine(默认情况下);
        打破;
}

如果 caseSwitch 1 ,永远达不到第二块,因为第一个块捕获它。

路线类遵循类似的模式(均<一个href=\"https://msdn.microsoft.com/en-us/library/system.web.routing.routebase.getroutedata(v=vs.110).aspx\"相对=nofollow> GetRouteData 和<一个href=\"https://msdn.microsoft.com/en-us/library/system.web.routing.routebase.getvirtualpath(v=vs.110).aspx\"相对=nofollow> GetVirtualPath 方法)。他们可以返回2种状态:


  1. 系统设定的路线值(或 VirtualPath 对象 GetVirtualPath 的情况下)。这表示路由相匹配的要求。

  2. 。这表示路由不匹配的要求。

在第一种情况下,MVC使用由路径产生的路由值来查找动作方法。在这种情况下,在 RouteTable 未分析任何进一步的

在第二种情况下,MVC将检查下一路线 RouteTable ,看它是否与匹配请求(内置行为URL和约束相匹配,但在技术上可以匹配在HTTP请求的任何东西)。再一次,这条路线可以返回一组 RouteValues​​ 根据结果。

错误配置示例

下面是我经常看到StackOverflow上张贴的经典例子(或它的某些变种):

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapRoute(
            名称:CustomRoute
            网址:{SEGMENT1} / {行动} / {ID}
            默认:新{控制器=myController的行动=索引,ID = UrlParameter.Optional}
        );        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}

在这个例子:


  1. CustomRoute 将匹配要么是1,2,或3段长度的任何URL(请注意, SEGMENT1 是必需的,因为它没有默认值)。

  2. 默认将匹配0,1,2,3段,长度任意URL。

因此​​,如果应用程序是通过网址 \\首页\\关于 CustomRoute 将匹配和供应以下 RouteValues​​ 来MVC:


  1. SEGMENT1 =家

  2. 控制器=myController的

  3. =动作关于

  4. ID = {}

这将使MVC查找名为名为控制器 MyControllerController 上一个动作关于,如果将失败不存在。在默认路线是在这种情况下,无法到达的执行路径,因为即使它会匹配2段URL,该框架将不给它机会,因为第一场比赛的胜利

杀青配置

有关于如何继续以固定的配置的几个选项。但他们都依赖于行为的第一场比赛胜,然后路由不继续看。

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

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapRoute(
            名称:CustomRoute
            网址:用户/ {}动作/(编号),            //注意,留下`action`和`id`出来的默认值
            //使他们必需的,因此该URL将只匹配,如果3
            //段随附的自定义或自定义开始时。
            //例:自定义/细节/ 343
            默认:新{控制器=myController的}
        );        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}

选项2:加1以上的正则表达式约束

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapRoute(
            名称:CustomRoute
            网址:{SEGMENT1} / {segment2} / {行动} / {ID}
            默认:新{控制器=myController的},
            限制:新{SEGMENT1 = @房子|汽车|巴士}
        );        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}

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

 公共类CorrectDateConstraint:IRouteConstraint
{
    公共BOOL匹配(HttpContextBase HttpContext的,路由路径,字符串参数名称,RouteValueDictionary价值,RouteDirection routeDirection)
    {
        VAR年=值[年作为字符串;
        VAR月=值[月作为字符串;
        VAR天=值[日]作为字符串;        日期时间theDate;
        返回DateTime.TryParse(年+ - +月+ - +天,System.Globalization.CultureInfo.InvariantCulture,DateTimeStyles.None,出theDate);
    }
}公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapRoute(
            名称:CustomRoute
            网址:{一年} / {月} / {天} / {}条,
            默认:新{控制器=新闻,行动=ArticleDetails},
            限制:新的一年{=新CorrectDateConstraint()}
        );        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}

选项4:制作的必需的段+制作的段数不匹配现有路线

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.MapRoute(
            名称:CustomRoute
            网址:{SEGMENT1} / {segment2} / {行动} / {ID}
            默认:新{控制器=myController的}
        );        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}

在上述情况下,在 CustomRoute 将仅匹配4段的URL(注意,这些可以是任何值)。因为只有在默认路由匹配的网址0,1,2,或3段。因此,不存在不可达执行路径。

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

这是路由不支持开箱即用(如在一个特定的域或子域匹配),任何东西都可以通过<一做href=\"http://stackoverflow.com/questions/31934144/multiple-levels-in-mvc-custom-routing/31958586#31958586\">implementing你自己的 RouteBase 子或路由子类。这也是理解如何/为什么路由工作方式是这样的最佳方式。

 公共类SubdomainRoute:路线
{
    公共SubdomainRoute(字符串URL):基地(网址,新MvcRouteHandler()){}    公众覆盖的RouteData GetRouteData(HttpContextBase的HttpContext)
    {
        VAR的RouteData = base.GetRouteData(HttpContext的);
        如果(的RouteData == NULL)返回NULL; //只看子,如果这条路摆在首位匹配。
        串子= httpContext.Request.Params [子域]; //指定为查询参数的子域接受precedence过的主机名。
        如果(子域== NULL){
            字符串主机= httpContext.Request.Headers [主机];
            INT指数= host.IndexOf('。');
            如果(索引&gt; = 0)
                子域= host.Substring(0,索引);
        }
        如果(子!= NULL)
            routeData.Values​​ [子域] =子;
        返回的RouteData;
    }    公众覆盖VirtualPathData GetVirtualPath(RequestContext的的RequestContext,RouteValueDictionary值)
    {
        反对subdomainParam = requestContext.HttpContext.Request.Params [子域];
        如果(subdomainParam!= NULL)
            值[子域] = subdomainParam;
        返回base.GetVirtualPath(RequestContext的,值);
    }
}

这个类是从借来的:<一href=\"http://stackoverflow.com/questions/278668/is-it-possible-to-make-an-asp-net-mvc-route-based-on-a-subdomain#15287579\">Is有可能使基于子域的ASP.NET MVC路线?

 公共类RouteConfig
{
    公共静态无效的RegisterRoutes(RouteCollection路线)
    {
        routes.IgnoreRoute({}资源个.axd / {*} PATHINFO);        routes.Add(新SubdomainRoute(URL:某处/独一无二));        routes.MapRoute(
            名称:默认,
            网址:{控制器} / {行动} / {ID}
            默认:新{控制器=家,行动=索引,ID = UrlParameter.Optional}
        );
    }
}


  

注意:这里真正的疑难杂症是,大多数人认为他们的路线都应该看起来像默认路线。复制,粘贴,做,对不对?错了。


  
  

有2的问题,这种方法通常出现的:


  
  

      
  1. pretty多少每隔路线应该至少有一个文字段(或约束,如果你是到诸如此类的事情)。

  2.   
  3. 最合乎逻辑的行为通常是为了使线路具有与其他的需要的段。

  4.   

  
  

另一种常见的误解是可选的细分意味着你可以忽略的任何的段,但在现实中你只能离开了最右边的段或部分。


  
  

微软成功地使基于约定的路由,可扩展,功能强大。他们未能使其直观明了。几乎每个人都失败他们第一次尝试(我知道我做到了!)。幸运的是,一旦你了解它是如何工作是不是很困难的。


From the www:"...The routing engine will take the first route that matches the supplied URL and attempt to use the route values in that route. Therefore, less common or more specialized routes should be added to the table first, while more general routes should be added later on..."

Why should I map specialized routes first? Someone can give me an example please where I can see the failing of "map common route first" ?

Thanks!

解决方案

The routing engine will take the first route that matches the supplied URL and attempt to use the route values in that route.

The reason why this happens is because the RouteTable is used like a switch-case statement. Picture the following:

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

If caseSwitch is 1, the second block is never reached because the first block catches it.

Route classes follow a similar pattern (in both the GetRouteData and GetVirtualPath methods). They can return 2 states:

  1. A set of route values (or a VirtualPath object in the case of GetVirtualPath). This indicates the route matched the request.
  2. null. This indicates the route did not match the request.

In the first case, MVC uses the route values that are produced by the route to lookup the Action method. In this case, the RouteTable is not analyzed any further.

In the second case, MVC will check the next Route in the RouteTable to see if it matches with the request (the built in behavior matches the URL and constraints, but technically you can match anything in the HTTP request). And once again, that route can return a set of RouteValues or null depending on the result.

Misconfiguration Example

Here is the classic example that I frequently see posted on StackOverflow (or some variant of it):

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

In this example:

  1. CustomRoute will match any URL that is either 1, 2, or 3 segments in length (note that segment1 is required because it has no default value).
  2. Default will match any URL that is 0, 1, 2, or 3 segments in length.

Therefore, if the application is passed the URL \Home\About, the CustomRoute will match, and supply the following RouteValues to MVC:

  1. segment1 = "Home"
  2. controller = "MyController"
  3. action = "About"
  4. id = {}

This will make MVC look for an action named About on a controller named MyControllerController, which will fail if it doesn't exist. The Default route is an unreachable execution path in this case because even though it will match a 2-segment URL, the framework will not give it the opportunity to because the first match wins.

Fixing the Configuration

There are several options on how to proceed to fix the configuration. But all of them depend on the behavior that the first match wins and then routing won't look any further.

Option 1: Add one or more Literal Segments

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

Option 2: Add 1 or more RegEx Constraints

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" },
            constraints: new { segment1 = @"house|car|bus" }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Option 3: Add 1 or more Custom Constraints

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

Option 4: Make Required Segments + Make the Number of Segments not Match Existing Routes

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

In the above case, the CustomRoute will only match a URL with 4 segments (note these can be any values). The Default route as before only matches URLs with 0, 1, 2, or 3 segments. Therefore there is no unreachable execution path.

Option 5: Implement RouteBase (or Route) for Custom Behavior

Anything that routing doesn't support out of the box (such as matching on a specific domain or subdomain) can be done by implementing your own RouteBase subclass or Route subclass. It is also the best way to understand how/why routing works the way it does.

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

This class was borrowed from: 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 }
        );
    }
}

NOTE: The real gotcha here is that most people assume that their routes should all look like the Default route. Copy, paste, done, right? Wrong.

There are 2 problems that commonly arise with this approach:

  1. Pretty much every other route should have at least one literal segment (or a constraint if you are into that sort of thing).
  2. The most logical behavior is usually to make the rest of the routes have required segments.

Another common misconception is that optional segments mean you can leave out any segment, but in reality you can only leave off the right-most segment or segments.

Microsoft succeeded in making routing convention-based, extensible, and powerful. They failed in making it intuitive to understand. Virtually everyone fails the first time they try it (I know I did!). Fortunately, once you understand how it works it is not very difficult.

这篇关于为什么要特别路线在asp.net mvc的共同路线之前首先映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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