为什么“默认”站点路由到ASP.NET MVC中的站点根目录?

时间:2016-07-03 15:50:11

标签: asp.net-mvc asp.net-mvc-5 asp.net-mvc-routing

ASP.NET MVC站点附带以下默认root,在访问站点的根目录时执行Home控制器的Index操作(例如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 / Index?

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

我看过很多文章都涉及到路由 - 但不是解释这个问题的文章。

相关问题

在其他情况下,'默认'中的内容似乎更像是路线的映射。例如

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

这只是在bongo控制器上执行宾果动作 - 只要输入了确切的URL“abc / def”。为什么称它为“默认值” - 这个术语似乎并不适合。 (删除'默认值'有没有效果,我已经看到它被省略了。)

在默认路线中,它似乎更像是后备,在后一个例子中更像是一个映射?

我觉得我在这里缺少概念层面的东西。

THX。

1 个答案:

答案 0 :(得分:2)

逻辑虽然起初不是很直观,但实际上非常简单。

通常,当传入请求发生时,会发生两种不同的事情。

  1. 尝试匹配路线
  2. 为MVC提供一组路由值(以及可选的路由元数据),用于查找操作方法
  3. 当传入请求发生时,MVC在路由表中执行第一条路由的GetRouteData方法。如果它不匹配,它将尝试第二个,第三个,依此类推,直到找到匹配为止。

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

    匹配过程依赖于三件事:

    1. 占位符
    2. 文字片段
    3. 约束
    4. 占位符

      您要问的部分是占位符以及没有值时它们匹配的原因。占位符(即{controller})就像变量一样。他们会接受任何价值。它们可以初始化为默认值。如果它们未初始化为默认值,则必须位于URL 中才能匹配。

      考虑路线定义中的defaults

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

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

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

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

      网址:/

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

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

      网址/Home

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

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

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

        

      网址:/Test

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

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

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

      这就是您在上面提供的网址/a/b无法正常工作的原因 - 您的项目中没有名为AController的控制器,其操作名为B

        

      如果占位符未初始化为默认值,则必须位于URL 中才能使路径匹配。

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

      因此,根据上述路线,它将匹配网址/Home

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

      但它不会匹配网址/,因为控制器没有默认值。

      文字细分

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

      以上路线在网址中使用文字段。文字段需要精确匹配(不区分大小写)才能将路径视为匹配。因此,唯一匹配的网址是abc/def或这两个网段的任何大写/小写组合。

      但是,这种情况在一方面有所不同。 无法通过网址传递值。因此,必须设置默认值(至少为controlleraction),以便将任何路由值传递给MVC。

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

      MVC框架要求BongoController有一个名为Bingo的动作,否则这条路线将失败。

      约束

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

      RegEx constraints

      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
      • /

      Custom constraints

      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

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

        

      文字段(或带有占位符的部分文字段),约束和所需值通常都是非常好在路由设置中使用的东西。它们有助于确保您的路线在如此宽的范围内不匹配,以阻止在路线表中执行在其后面注册的路线。

           

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

      进一步阅读