【问题标题】:ASP.NET MVC Routing with Default Controller带有默认控制器的 ASP.NET MVC 路由
【发布时间】:2010-12-15 08:44:01
【问题描述】:

对于一个场景,我有一个 ASP.NET MVC 应用程序,其 URL 如下所示:

http://example.com/Customer/List
http://example.com/Customer/List/Page/2
http://example.com/Customer/List
http://example.com/Customer/View/8372
http://example.com/Customer/Search/foo/Page/5

这些网址是通过Global.asax.cs中的以下路由实现的

routes.MapRoute(
    "CustomerSearch"
    , "Customer/Search/{query}/Page/{page}"
    , new { controller = "Customer", action = "Search" }
);

routes.MapRoute(
    "CustomerGeneric"
    , "Customer/{action}/{id}/Page/{page}"
    , new { controller = "Customer" }
);

//-- Default Route
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Customer", action = "Index", id = "" }
);

这些都进展顺利,直到一个新的需求出现并想要从 URL 中删除关键字“客户”,使 URL 看起来像:

http://example.com/List
http://example.com/List/Page/2
http://example.com/List
http://example.com/View/8372
http://example.com/Search/foo/Page/5

编辑: 更正了示例链接,感谢@haacked。

我尝试添加新的MapRoutes 以仅采用{action} 并将默认控制器设置为客户。例如/

routes.MapRoute(
    "CustomerFoo"
    , "{action}"
    , new { controller = "Customer", action = "Index" }
);

这似乎可行,但是现在由 Html.ActionLink() 生成的所有链接都很奇怪并且不再是 URL 友好的。

那么,这可以实现吗?我的方向是否正确?

【问题讨论】:

  • 你删除了旧路由吗?
  • 不,我将新路线放在原始路线之前。

标签: asp.net-mvc routing


【解决方案1】:

不要将"{action}/{id}""{controller}/{action}/{id}" 之类的规则混用...特别是当后面的 id 具有默认值(即可选)时。

在这种情况下,您没有任何东西可以让路由知道哪个是正确的。

如果您需要,一种解决方法是在前面的操作中添加一个约束(请参阅this)到一组值,即列表、视图。当然,使用这些类型的规则,您不能拥有与操作同名的控制器。

还请记住,如果您在 "{action}/{id}" 规则中指定默认操作和 ID,则当您点击您的站点的路线时将使用该操作。

【讨论】:

  • 抱歉我的回复速度不快,因为我想在回复反馈之前先尝试一下答案。对此感到抱歉:P
  • @rockacola it k,不急。
  • 这就是我要找的!路线约束将为我提供我想要的自定义,而不会危及 MapRoute。我需要重新组织我所有现有的路线。谢谢@Freddy!
【解决方案2】:

为什么新列表中的第一个 URL 仍然有“客户”。我认为这是一个错字,你的意思是:

以下路线适合我:

routes.MapRoute(
    "CustomerSearch"
    , "Search/{query}/Page/{page}"
    , new { controller = "Customer", action = "Search" }
);

routes.MapRoute(
    "CustomerGeneric"
    , "{action}/{id}/Page/{page}"
    , new { controller = "Customer" }
);

//-- Default Route
routes.MapRoute(
    "Default",
    "{action}/{id}",
    new { controller = "Customer", action = "Index", id = "" }
);

您是如何生成链接的。由于控制器不再在您的路由的 URL 中(也就是您在路由 URL 中没有“{controller}”),但它是默认值,因此您需要确保在生成路由时指定控制器。

因此而不是

Html.ActionLink("LinkText", "ActionName")

Html.ActionLink("LinkText", "ActionName", "Customer")

为什么?假设您有以下路线。

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

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

当你调用这个时,你指的是哪条路线?

<%= Html.ActionLink("LinkText", "ActionName") %>

您可以通过指定控制器来区分,我们将选择具有与指定控制器匹配的默认值的控制器。

【讨论】:

  • 感谢菲尔的回复。 如果所有页面都来自客户控制器,路由工作正常。假设我有 User 控制器和 SignIn 操作:/User/SignIn/ 请求将被 CustomerDefault (即 {action}/{id})劫持,我希望它落入默认(即 {controller}/{action})。有没有办法教你的路线哪个参数是action,哪个参数是controller?再次感谢,喜欢你的博客。
  • @rockacola 我刚刚添加了一个关于这个的答案(在看到你的评论之前),添加一个约束来限制 {action},这样它就不会对 {customer} 使用该规则。
  • 使用约束,就像 Freddy 说的那样。
【解决方案3】:

你可以create a route that is constrained to only match actions in your Customer controller

public static class RoutingExtensions {
    ///<summary>Creates a route that maps URLs without a controller to action methods in the specified controller</summary>
    ///<typeparam name="TController">The controller type to map the URLs to.</typeparam>
    public static void MapDefaultController<TController>(this RouteCollection routes) where TController : ControllerBase {
        routes.MapControllerActions<TController>(typeof(TController).Name, "{action}/{id}", new { action = "Index", id = UrlParameter.Optional });
    }
    ///<summary>Creates a route that only matches actions from the given controller.</summary>
    ///<typeparam name="TController">The controller type to map the URLs to.</typeparam>
    public static void MapControllerActions<TController>(this RouteCollection routes, string name, string url, object defaults) where TController : ControllerBase {
        var methods = typeof(TController).GetMethods()
                                         .Where(m => !m.ContainsGenericParameters)
                                         .Where(m => !m.IsDefined(typeof(ChildActionOnlyAttribute), true))
                                         .Where(m => !m.IsDefined(typeof(NonActionAttribute), true))
                                         .Where(m => !m.GetParameters().Any(p => p.IsOut || p.ParameterType.IsByRef))
                                         .Select(m => m.GetActionName());

        routes.Add(name, new Route(url, new MvcRouteHandler()) {
            Defaults = new RouteValueDictionary(defaults) { { "controller", typeof(TController).Name.Replace("Controller", "") } },
            Constraints = new RouteValueDictionary { { "action", new StringListConstraint(methods) } }
        });
    }

    private static string GetActionName(this MethodInfo method) {
        var attr = method.GetCustomAttribute<ActionNameAttribute>();
        if (attr != null)
            return attr.Name;
        return method.Name;
    }

    class StringListConstraint : IRouteConstraint {
        readonly HashSet<string> validValues;
        public StringListConstraint(IEnumerable<string> values) { validValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase); }

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
            return validValues.Contains(values[parameterName]);
        }
    }

    #region GetCustomAttributes
    ///<summary>Gets a custom attribute defined on a member.</summary>
    ///<typeparam name="TAttribute">The type of attribute to return.</typeparam>
    ///<param name="provider">The object to get the attribute for.</param>
    ///<returns>The first attribute of the type defined on the member, or null if there aren't any</returns>
    public static TAttribute GetCustomAttribute<TAttribute>(this ICustomAttributeProvider provider) where TAttribute : Attribute {
        return provider.GetCustomAttribute<TAttribute>(false);
    }
    ///<summary>Gets the first custom attribute defined on a member, or null if there aren't any.</summary>
    ///<typeparam name="TAttribute">The type of attribute to return.</typeparam>
    ///<param name="provider">The object to get the attribute for.</param>
    ///<param name="inherit">Whether to look up the hierarchy chain for attributes.</param>
    ///<returns>The first attribute of the type defined on the member, or null if there aren't any</returns>
    public static TAttribute GetCustomAttribute<TAttribute>(this ICustomAttributeProvider provider, bool inherit) where TAttribute : Attribute {
        return provider.GetCustomAttributes<TAttribute>(inherit).FirstOrDefault();
    }
    ///<summary>Gets the custom attributes defined on a member.</summary>
    ///<typeparam name="TAttribute">The type of attribute to return.</typeparam>
    ///<param name="provider">The object to get the attribute for.</param>
    public static TAttribute[] GetCustomAttributes<TAttribute>(this ICustomAttributeProvider provider) where TAttribute : Attribute {
        return provider.GetCustomAttributes<TAttribute>(false);
    }
    ///<summary>Gets the custom attributes defined on a member.</summary>
    ///<typeparam name="TAttribute">The type of attribute to return.</typeparam>
    ///<param name="provider">The object to get the attribute for.</param>
    ///<param name="inherit">Whether to look up the hierarchy chain for attributes.</param>
    public static TAttribute[] GetCustomAttributes<TAttribute>(this ICustomAttributeProvider provider, bool inherit) where TAttribute : Attribute {
        if (provider == null) throw new ArgumentNullException("provider");

        return (TAttribute[])provider.GetCustomAttributes(typeof(TAttribute), inherit);
    }
    #endregion
}

【讨论】:

    猜你喜欢
    • 2012-08-14
    • 2010-11-01
    • 2011-07-15
    • 2016-10-07
    • 1970-01-01
    • 2017-07-08
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多