为什么要在asp.net mvc中的常用路线之前先映射特殊路线?
从www:“…路由引擎将采取与提供的URL相匹配的第一个路由,并尝试使用该路由中的路由值,因此,应该首先添加不常见或更专用的路由,而更多一般路线应该稍后添加…“
为什么我应该先映射专门的路线? 有人可以举一个例子,我可以看到“地图通用路线第一”的失败吗?
谢谢!
路由引擎将采用与提供的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; }
如果caseSwitch
是1
,则第二个块永远不会到达,因为第一个块捕获它。
Route
类遵循类似的模式(在GetRouteData
和GetVirtualPath
方法中)。 他们可以返回2个状态:
- 一组路由值(或
GetVirtualPath
情况下的VirtualPath
对象)。 这表示路线匹配请求。 -
null
。 这表示路线与请求不符。
在第一种情况下,MVC使用路由产生的路由值来查找Action
方法。 在这种情况下, RouteTable
不会被进一步分析。
在第二种情况下,MVC将检查RouteTable
的下一个Route
是否与请求匹配(内置行为与URL和约束匹配,但技术上可以匹配HTTP请求中的任何内容)。 再次,该路由可以返回一组RouteValues
或null
取决于结果。
如果您尝试使用上面的switch-case语句,程序将不会编译。 然而,如果你configuration了一个从不返回null
的路由,或者在比它应该更多的情况下返回一个RouteValues
对象,那么这个程序将会被编译,但是会不正常。
错误configuration示例
下面是我经常看到的在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(请注意,段1是必需的,因为它没有默认值)。 -
Default
将匹配任何长度为0,1,2或3段的URL。
因此,如果应用程序通过URL \Home\About
,则CustomRoute
将匹配,并将以下RouteValues
给MVC:
-
segment1 = "Home"
-
controller = "MyController"
-
action = "About"
-
id = {}
这将使MVC在一个名为MyControllerController
的控制器上寻找名为About
的动作,如果它不存在,将会失败。 在这种情况下, Default
路由是无法访问的执行path,因为即使它将匹配2段的URL,框架也不会给它机会,因为第一场比赛胜利了。
修复configuration
关于如何继续修复configuration有几个选项。 但是所有这些都取决于第一场比赛获胜的行为,然后路线就不会再进一步了。
选项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}/{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 } ); } }
选项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段的URL(注意这些值可以是任意值)。 如前所述的Default
路由只匹配0,1,2或3段的URL。 因此没有不可达的执行path。
选项5:为自定义行为实施RouteBase(或Route)
路由不支持开箱即用的任何事情(例如匹配特定的域或子域)可以通过实现您自己的RouteBase
子类或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); } }
这个类是从以下借来的: 是否有可能使一个基于子域的ASP.NET MVC路线?
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
路线。 复制,粘贴,完成,对不对? 错误。这种方法通常会出现两个问题:
- 几乎每一个其他的路线应该至less有一个文字段(或者如果你是这样的事情约束)。
- 最合乎逻辑的行为通常是让其余的路线具有所需的细分。
另一个常见的误解是可选的部分意味着你可以忽略任何部分,但实际上,你只能离开最右边的部分或部分。
微软成功地使基于约定的,可扩展的和强大的。 他们没有使直观的理解。 几乎每个人都第一次尝试失败(我知道我做到了!)。 幸运的是,一旦你了解它是如何工作的,这不是很困难。
- 如何获取ASP.NET MVC中的当前用户
- WebAPI中的DependencyResolver.SetResolver和HttpConfiguration.DependencyResolver之间有什么区别
- 如何从基本控制器的OnActionExecutingredirect?
- 如何使用ASP.NET MVC和AngularJS路由?
- 如何扩展User.Identity的可用属性
- 使用entity framework的最有效的自引用树的方法
- 在HtmlHelper的扩展方法中从传递的lambdaexpression式获取属性值的最简单方法是什么?
- 无法findtypes或名称空间名称“DbContext”
- Viewbag检查是否存在项目并写出html和值错误