【问题标题】:Problems with creating two routes which won't generate 404 error in ASP.NET MVC在 ASP.NET MVC 中创建不会生成 404 错误的两条路由的问题
【发布时间】:2015-12-31 09:56:15
【问题描述】:

我正在尝试使用路由构建我的教程项目。我的主要目标是建立两条在任何情况下都不会产生 404 错误的路线。我的意思是,如果路径错误,我希望路由使用 /Home/Index 路径。我有以下两条路线 -

    routes.MapRoute("Default", "{controller}/{action}", 
                        new {controller = "Home", action = "Index"}
                        );

    routes.MapRoute("Second", "{*catchall}",
                        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
                        );

当我使用与第一条路线不匹配的不存在路径时,它可以正常工作,就像这样 -

但如果是这样,那么我有以下 -

我理解它发生的原因。但是目前,我只能设法找到“某种”解决方案。将以下代码添加到 web.config 文件 -

<customErrors mode="On">
      <error statusCode="404" redirect="~/Home/Index"/>
</customErrors>

但是,我认为这不是解决此问题的最佳方法。因为,据我所知,它只是简单地捕获所有错误并将其重定向到正确的路径,而无需实际使用路由。所以我认为我不需要这种全局处理。

所以有人可以给我一个提示或为我的问题提供一个好的解决方案。谢谢。

【问题讨论】:

  • 嗯......这个问题已经很老了。大多数答案使用 MVC 1 或 MVC 2。所以也许存在更好的解决方案......其中一些还使用第三方框架/库,但我不想使用它们。

标签: c# asp.net-mvc routes web-config asp.net-mvc-routing


【解决方案1】:

请用

更改路线声明
 routes.MapRoute("Second", "{*catchall}",
                        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
                        );

routes.MapRoute("Default", "{controller}/{action}", 
                        new {controller = "Home"`enter code here`, action = "Index"}

                    );

【讨论】:

  • 其他控制器,即使是正确的,也会停止工作。
  • 是的,你不能在路由配置的顶部放置一个 catch all 路由,否则它会真正捕获所有的东西。
【解决方案2】:

您可以使用自定义RouteConstraint 来做到这一点。

首先,像这样设置你的路线:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

        );

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

现在您可以创建ControllerNameConstraint。我在这里做了一个 非常 简单的实现,您需要对其进行更改,以便它与您想要实现的目标一起工作(如果您不想这样做,您有很多使用反射的选项随时更新)

public class ControllerNameConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (values[parameterName].ToString() == "Home" || values[parameterName].ToString() == "Account")
        {
            //the route is matched and will run the constrained ("Default") route
            return true;
        }
        //the route is not matched to the constraint and will fall through to your wildcard route
        return false;
    }
}

【讨论】:

    【解决方案3】:

    我正在做一些非常类似的事情,试图基于数据库控制的站点导航创建我自己的动态路由。从本质上讲,我希望任何到达真正定义的路由的东西都能通过正常的路由过程,但是我可以让内容页面等内容的 URL 完全由它们在导航中的位置控制。无论如何,为此我依赖于httpErrors Web.config 声明:

    <system.webServer>
        ...
        <httpErrors errorMode="Custom" existingResponse="Replace">
            <remove statusCode="404" />
            <error statusCode="404" responseMode="ExecuteURL" path="/error/404" />
        </httpErrors>
    

    然后,我有一个ErrorController,其中包含处理 404 的操作。在该操作中,我根据数据库检查尝试的 URL,如果找到匹配的项目,我会将请求移交给适当的位置。如果没有匹配,那么我只返回一个视图,这是我设置的自定义 404 视图。上面的路径部分必须是您的 404 操作的 URL。我的是/error/404,因为我正在使用属性路由并且可以随心所欲。如果您依赖默认路由,则不能有名为 404 的操作,因此它必须类似于 /error/http404/error/notfound

    【讨论】:

      【解决方案4】:

      好吧,你并没有真正定义什么是“错误”路由,所以这是我的定义:

      1. 控制器或动作在项目中不存在。
      2. 如果传递了“id”,则它必须存在于操作方法中。

      路线

      我使用约束来做到这一点。 AFAIK,不可能对可选参数使用约束。这意味着为了使id 参数可选,您需要3 个路由。

      routes.MapRoute(
         name: "DefaultWithID",
         url: "{controller}/{action}/{id}",
         defaults: new { controller = "Home", action = "Index" },
         constraints: new { action = new ActionExistsConstraint(), id = new ParameterExistsConstraint() }
      );
      
      routes.MapRoute(
         name: "Default",
         url: "{controller}/{action}",
         defaults: new { controller = "Home", action = "Index" },
         constraints: new { action = new ActionExistsConstraint() }
      );
      
      routes.MapRoute(
          name: "Second",
          url: "{*catchall}",
          defaults: new { controller = "Home", action = "Index" }
      );
      

      ActionExistsConstraint

      public class ActionExistsConstraint : IRouteConstraint
      {
          public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
          {
              if (routeDirection == RouteDirection.IncomingRequest)
              {
                  var action = values["action"] as string;
                  var controller = values["controller"] as string;
      
                  var thisAssembly = this.GetType().Assembly;
      
                  Type[] types = thisAssembly.GetTypes();
      
                  Type type = types.Where(t => t.Name == (controller + "Controller")).SingleOrDefault();
      
                  // Ensure the action method exists
                  return type != null && type.GetMethod(action) != null;
              }
      
              return true;
          }
      }
      

      ParameterExistsConstraint

      public class ParameterExistsConstraint : IRouteConstraint
      {
          public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
          {
              if (routeDirection == RouteDirection.IncomingRequest)
              {
                  var action = values["action"] as string;
                  var controller = values["controller"] as string;
      
                  var thisAssembly = this.GetType().Assembly;
      
                  Type[] types = thisAssembly.GetTypes();
      
                  Type type = types.Where(t => t.Name == (controller + "Controller")).SingleOrDefault();
                  var method = type.GetMethod(action);
      
                  if (type != null && method != null)
                  {
                      // Ensure the parameter exists on the action method
                      var param = method.GetParameters().Where(p => p.Name == parameterName).FirstOrDefault();
                      return param != null;
                  }
                  return false;
              }
      
              return true;
          }
      }
      

      【讨论】:

      • 非常感谢您的回复! “错误”路由是指从 URL 提供的 {controller}/{action} 不存在,正如您在第二张和第三张图片中看到的那样。
      • 那么可选的“id”参数呢?如果方法上不存在主页是否应该返回?或者即使缺少参数也应该调用动作?
      【解决方案5】:

      您指定默认路由的方式是查找任何控制器/动作配对,以及是否找不到它们以替换默认值。如果您在创建地图时调用了确切的回家路线并在 url 中省略了控制器和操作关键字,它将仅匹配这些关键字,而所有其他关键字都将被您的全部捕获。

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

      我不知道您可能有任何其他路线,因此我无法根据您的确切情况进行测试。试试这个,看看它是否对你有帮助。

      【讨论】:

        【解决方案6】:

        NightOwl888 的答案对我有用,但是 ActionExistsConstraint 和 ParameterExistsConstraint 的代码需要稍作修改,以删除区分大小写的比较。这是我的新代码,它在我的情况下完美运行

        public class ActionExistsConstraint : IRouteConstraint
        {
            public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
            {
                if (routeDirection == RouteDirection.IncomingRequest)
                {
                    var action = values["action"] as string;
                    var controller = values["controller"] as string;
        
                    var thisAssembly = this.GetType().Assembly;
        
                    Type[] types = thisAssembly.GetTypes();
        
                    Type type = types.FirstOrDefault(t => t.Name.Equals(controller + "Controller", StringComparison.OrdinalIgnoreCase));
        
                    // Ensure the action method exists
                    return type != null &&
                           type.GetMethods().Any(x => x.Name.Equals(action, StringComparison.OrdinalIgnoreCase));
                }
        
                return true;
            }
        }
        
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
            {
                if (routeDirection == RouteDirection.IncomingRequest)
                {
                    var action = values["action"] as string;
                    var controller = values["controller"] as string;
        
                    var thisAssembly = this.GetType().Assembly;
        
                    Type[] types = thisAssembly.GetTypes();
        
                    Type type = types.FirstOrDefault(t => t.Name .Equals(controller + "Controller", StringComparison.OrdinalIgnoreCase));
                    var method = type.GetMethods().FirstOrDefault(x => x.Name.Equals(action, StringComparison.OrdinalIgnoreCase));
        
                    if (type != null && method != null)
                    {
                        // Ensure the parameter exists on the action method
                        var param = method.GetParameters().FirstOrDefault(p => p.Name.Equals(parameterName, StringComparison.OrdinalIgnoreCase));
                        return param != null;
                    }
                    return false;
                }
        
                return true;
            }
        

        【讨论】:

          猜你喜欢
          • 2016-07-30
          • 1970-01-01
          • 2014-10-16
          • 2015-02-24
          • 2011-10-25
          • 1970-01-01
          • 1970-01-01
          • 2010-10-30
          • 1970-01-01
          相关资源
          最近更新 更多