来自 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;
}
如果
caseSwitch
是 1
,第二个 block 永远不会到达,因为第一个 block 捕获了它。Route
类遵循类似的模式(在 GetRouteData
和 GetVirtualPath
方法中)。他们可以返回两种状态:VirtualPath
中的 GetVirtualPath
对象)。这表明路由匹配请求。 null
.这表明路由与请求不匹配。 在第一种情况下,MVC 使用路由生成的路由值来查找
Action
方法。在这种情况下,RouteTable
不再进一步分析。在第二种情况下,MVC 将检查下一个
Route
在 RouteTable
查看它是否与请求匹配(内置行为与 URL 和约束匹配,但从技术上讲,您可以匹配 HTTP 请求中的任何内容)。再一次,该路由可以返回一组 RouteValues
或 null
取决于结果。如果您尝试使用上述 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:
- Pretty much every other route should have at least one literal segment (or a constraint if you are into that sort of thing).
- 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/