为什么在asp.net mvc中先映射特殊路由,然后再映射普通路由? [英] Why map special routes first before common routes in asp.net mvc?

查看:120
本文介绍了为什么在asp.net mvc中先映射特殊路由,然后再映射普通路由?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从www:

...路由引擎将采用与所提供的URL匹配的第一条路由,并尝试使用该路由中的路由值.因此,应该先将不常见或更专用的路由添加到表中,而稍后再添加更通用的路由...

...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" ?

推荐答案

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

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

之所以发生这种情况,是因为RouteTable的用法类似于switch-case语句.图片如下:

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

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

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

Route类遵循类似的模式(在 GetVirtualPath 方法).他们可以返回2个状态:

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

  1. 一组路由值(在GetVirtualPath情况下为VirtualPath对象).这表明路由已符合请求.
  2. null.这表明路线与请求不匹配.
  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.

在第一种情况下,MVC使用由路由产生的路由值来查找Action方法.在这种情况下,RouteTable将不再进行分析.

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.

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

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.

如果您尝试使用上述的switch-case语句,则该程序将无法编译.但是,如果您配置的路由从不不会返回null或返回RouteValues对象的情况超出了应有的情况,则该程序将编译但行为不当.

If you try to use a switch-case statement as above, the program won't compile. However, if you configure a route that never returns null or returns a RouteValues object in more cases than it should, the program will compile, but will misbehave.

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

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

在此示例中:

  1. CustomRoute将匹配任何长度为1、2或3段的URL(请注意,由于segment1没有默认值,因此是必需的).
  2. Default将匹配任何长度为0、1、2或3段的URL.
  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.

因此,如果将URL \Home\About传递给应用程序,则CustomRoute将匹配,并将以下RouteValues提供给MVC:

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 = {}
  1. segment1 = "Home"
  2. controller = "MyController"
  3. action = "About"
  4. id = {}

这将使MVC在名为MyControllerController的控制器上寻找名为About的动作,如果该动作不存在,它将失败.在这种情况下,Default路由是无法到达的执行路径,因为即使它匹配2段URL,框架也不会给它机会,因为第一个匹配获胜.

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.

关于如何继续修复配置,有几个选项.但是所有这些都取决于首个比赛获胜的行为,然后路由看起来就不会再变了.

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.

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:添加1个或多个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:添加1个或多个自定义约束

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:细分为必填 +设置细分数与现有路线不匹配

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

在上述情况下,CustomRoute将仅与具有4个段的URL匹配(请注意,这些值可以是任何值).以前的Default路由仅匹配具有0、1、2或3段的URL.因此,没有不可达的执行路径.

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.

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

注意:这里的真正陷阱是,大多数人都认为他们的路线都应该看起来像Default路线.复制,粘贴,完成,对吗?不对.

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. 几乎所有其他路线都应至少具有一个文字部分(如果您喜欢这种东西,则应具有约束条件.)
  2. 最合乎逻辑的行为通常是使其余路由具有必需个分段.
  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成功地使基于路由约定的,可扩展的和强大的路由.他们未能使其直观易懂.几乎每个人都第一次尝试失败(我知道我做过!).幸运的是,一旦您了解了它是如何工作的,这并不是很难.

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天全站免登陆