为什么 "Default" 站点路由到 ASP.NET MVC 中的站点根目录有效?



ASP.NET MVC站点带有以下默认根,当访问该站点的根(例如http://localhost:12345)时,该根执行主控制器的索引操作

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

那么,当找不到路线时,这是一种倒退吗?

没那么快。如果我们尝试导航到http://localhost/a/b(其中不存在"a"控制器),它将不会执行Home控制器的Index操作-将返回错误。

为什么在这种情况下无法执行Home/Index,但在路径中完全不输入任何内容就可以执行Home/Inindex?

这里的逻辑是什么?为什么这条路线被称为"默认"?

我看过很多关于路由的文章,但没有一篇对此进行解释。

相关问题

在其他情况下,"默认值"似乎更像是一条路线的映射。例如

url: "abc/def", defaults: new { controller = "bongo", action = "bingo" }

这只是在bongo控制器上执行宾果游戏操作——只要输入了确切的url"abc/def"。为什么它被称为"违约"——这个词似乎并不合适。(删除"默认值"有任何效果吗,我看到它被省略了)。

在默认路由中,它看起来更像是一个回退,在后一个示例中,它更像是映射?

我觉得我在概念层面上缺少了一些东西。

thx。

逻辑虽然一开始不是很直观,但实际上非常简单。

一般来说,当传入请求发生时,会发生两种不同的情况。

  1. 尝试匹配路线
  2. 为MVC提供一组路由值(以及可选的路由元数据),用于查找操作方法

当传入请求发生时,MVC执行路由表中第一个路由的GetRouteData方法。如果不匹配,它将尝试第二次、第三次,依此类推,直到找到匹配。

如果最终在路由表中没有匹配,RouteCollection.GetRouteData(调用每条路由的GetRouteData的方法)将返回null。如果找到匹配,匹配路由中的路由值将返回到MVC,MVC在那里使用它们来查找控制器和要执行的操作。请注意,在这种情况下,不会检查路由表中的其他路由是否匹配。换句话说,第一场比赛总是赢。

匹配过程依赖于三件事:

  1. 占位符
  2. 文字段
  3. 限制

占位符

您询问的部分是占位符,以及为什么在没有值的情况下它们匹配。占位符(即{controller})的作用类似于变量。他们会接受任何价值。它们可以初始化为默认值。如果它们没有初始化为默认值,则需要位于URL中才能匹配。

考虑路线定义中的defaults

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

如果URL中未提供占位符,则将使用这些值(作为RouteValues中的匹配和输出)。

按照同样的逻辑,默认情况下,所有这些URL都将到达HomeController.Index操作方法。

  • /
  • /Home
  • /Home/Index
  • /Home/Index/Foo
  • /Home/Index/123

URL:/

如果将URL/传递到框架,它将匹配Default路由并将其发送到HomeController.Index方法,因为如果未提供默认值,则默认值为HomeIndex。在这种情况下,路由值为:

| Key         | Value       |
|-------------|-------------|
| controller  | Home        |
| action      | Index       |
| id          | {}          |

URL/Home

请注意,您也可以只传递控制器名称/Home。路由表看起来完全一样。

| Key         | Value       |
|-------------|-------------|
| controller  | Home        |
| action      | Index       |
| id          | {}          |

但是,在这种情况下,controller值是通过URL中的占位符传入的。它不再考虑路由的默认controller值,因为URL中已经提供了一个值。

URL:/Test

遵循相同的逻辑,URL/Test将产生以下路由表。

| Key         | Value       |
|-------------|-------------|
| controller  | Test        |
| action      | Index       |
| id          | {}          |

路由不会自动检查控制器是否真的存在。它只是提供了价值。如果应用程序中没有名为TestController的控制器执行Index操作,则会导致错误。

这就是为什么您上面提供的URL/a/b不起作用的原因——您的项目中没有名为AController的控制器,其操作名为B

如果占位符未初始化为默认值,则需要位于URL中,以便路由匹配。

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

因此,给定上述路由,它将匹配URL/Home:

| Key         | Value       |
|-------------|-------------|
| controller  | Home        |
| action      | Index       |
| id          | {}          |

但它与URL/不匹配,因为控制器并没有默认值。

文字段

routes.MapRoute(
name: "Foo",
url: "abc/def",
defaults: new { controller = "bongo", action = "bingo" }
);

上述路由在URL中使用文字段。为了将路由视为匹配,文字段需要完全匹配(不区分大小写)。因此,唯一匹配的URL是abc/def或这两个段的任何大写/小写组合。

然而,这种情况在一个方面有所不同。无法通过URL传递值。因此,必须设置默认值(至少对于controlleraction),以便将任何路由值传递给MVC。

| Key         | Value       |
|-------------|-------------|
| controller  | bongo       |
| action      | bingo       |

MVC框架要求有一个BongoController和一个名为Bingo的操作,否则这个路由将失败得很惨。

限制

约束是路由匹配所需的额外条件。每个约束返回一个布尔值(匹配/不匹配)响应。每条管线可以有0到多个约束。

RegEx约束

routes.MapRoute(
name: "CustomRoute",
url: "{placeholder1}/{action}/{id}",
defaults: new { controller = "MyController" },
constraints: new { placeholder1 = @"^house$|^car$|^bus$" }
);

匹配

  • /house/details/123
  • /car/foo/bar
  • /car/bar/foo

与不匹配

  • /house/details
  • /bank/details/123
  • /bus/foo
  • /car
  • /

自定义约束

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);
}
}
routes.MapRoute(
name: "CustomRoute",
url: "{year}/{month}/{day}/{article}",
defaults: new { controller = "News", action = "ArticleDetails" },
constraints: new { year = new CorrectDateConstraint() }
);

注意:分配给约束的值(在上述情况下为year =)与传递给自定义约束的值相同。但是,自定义约束没有义务使用此值。有时,使用任何值都没有意义,在这种情况下,可以在controller上设置约束。

匹配

  • /2012/06/20/some-great-article
  • /2016/12/25/all-about-christmas

与不匹配

  • /2012/06/33/some-great-article
  • /2012/06/20
  • /99999/09/09/the-foo-article

在大多数情况下,只要URL中有占位符(如{controller}{something}),就应该使用约束,以防止它们与不应该匹配的值匹配。

文本段(或带有占位符的部分文本段)、约束和所需值通常都是在路由设置中使用的非常好的内容。它们有助于确保您的路由不会在如此大的范围内匹配,从而阻止执行在路由表中在它们之后注册的路由。

占位符与任何值匹配,因此通常不建议在任何路由中仅使用占位符,除非与约束一起使用,否则应在Default路由中使用占位符。StackOverflow上的许多人建议完全删除Default路由,以确保意外路由不会起作用,我不一定不同意这种观点。

进一步阅读

  • ASP.NET路由(MSDN)
  • 为什么在asp.net mvc中,先绘制特殊路线,然后再绘制常见路线