asp.net-mvc - 为什么在 asp.net mvc 中的公共(public)路由之前先映射特殊路由?

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

来自 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...



为什么要先绘制专门的路线?有人可以给我一个例子,我可以在哪里看到“首先映射公共(public)路线”的失败?

最佳答案

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 语句一样使用。如下图:
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 ,第二个 block 永远不会到达,因为第一个 block 捕获了它。
Route类遵循类似的模式(在 GetRouteData GetVirtualPath 方法中)。他们可以返回两种状态:
  • 一组路由值(或 VirtualPath 中的 GetVirtualPath 对象)。这表明路由匹配请求。
  • null .这表明路由与请求不匹配。

  • 在第一种情况下,MVC 使用路由生成的路由值来查找 Action方法。在这种情况下,RouteTable不再进一步分析。

    在第二种情况下,MVC 将检查下一个 RouteRouteTable查看它是否与请求匹配(内置行为与 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 }
            );
        }
    }
    

    在这个例子中:
  • CustomRoute将匹配任何长度为 1、2 或 3 段的 URL(注意 segment1 是必需的,因为它没有默认值)。
  • Default将匹配长度为 0、1、2 或 3 段的任何 URL。

  • 因此,如果应用程序通过 URL \Home\About , CustomRoute将匹配,并提供以下 RouteValues到 MVC:
  • segment1 = "Home"
  • controller = "MyController"
  • action = "About"
  • id = {}

  • 这将使 MVC 查找名为 About 的操作。在名为 MyControllerController 的 Controller 上, 如果它不存在,它将失败。 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:添加 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:制作所需的路段 + 使路段数与现有路线不匹配
    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。因此没有不可达的执行路径。

    选项 5:为自定义行为实现 RouteBase(或 Route)

    implementing your own RouteBase subclass 路由不支持开箱即用的任何事情(例如在特定域或子域上匹配)都可以完成。或路线子类。这也是了解路由如何/为何以这种方式工作的最佳方式。
    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 }
            );
        }
    }
    

    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 - 为什么在 asp.net mvc 中的公共(public)路由之前先映射特殊路由?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35661062/

    相关文章:

    joomla - 如何从 joomla 1.6 中的文章 ID 获取菜单项的 url?

    javascript - 当从 Laravel 中的资源 Controller 访问 View 时,Vue.js 中的 $http.get 不起作用

    c# - 覆盖 ASP.NET 缓存策略并将 Cache-Control 设置为 public

    c# - CDN 路径在 MVC 中的 js 捆绑中不起作用

    c# - 在将 OnModelCreating 与 ApplicationDbContext 一起使用时解决 'No key Defined' 错误?

    asp.net-mvc - 允许匿名属性不起作用 MVC 5

    scala - Play 框架解析查询参数中的分号

    c# - MySqlMembershipProvider 的表前缀

    asp.net-mvc - 更新 Linq2Sql 对象在 MVC 中出现异常

    css - 推特 tyepahead CSS 渲染问题