asp.net-mvc 带有默认控制器的 ASP.NET MVC 路由

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1713379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:56:27  来源:igfitidea点击:

ASP.NET MVC Routing with Default Controller

asp.net-mvcrouting

提问by Trav L

For a scenario, I have a ASP.NET MVC application with URLs that look like the following:

对于一个场景,我有一个 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

These URLs are achieved with following routes in Global.asax.cs

这些 URL 是通过以下路由实现的 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 = "" }
);

These all have gone well until a new requirement arrived and wants to drop keyword 'Customer' off the URL, to make the URLs look like:

在出现新需求并希望从 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

Edit:corrected example links, thanks to @haacked.

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

I tried to add new MapRoutesto take {action}only and have default controller set to Customer. eg/

我尝试添加 new MapRoutesto take {action}only 并将默认控制器设置为 Customer。例如/

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

This seems to work, however now all links generated by Html.ActionLink() are weird and no longer URL friendly.

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

So, is this achievable? Am I approaching in the right direction?

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

回答by eglasius

don't mix a rule like: "{action}/{id}"with one that's "{controller}/{action}/{id}"... specially when id in the later has a default value i.e. is optional.

不要混合这样的规则:"{action}/{id}"与一个"{controller}/{action}/{id}"...特别是当 id 在后面有一个默认值 ie 是可选的。

In that case you have nothing that allows routing to know which one is the right one to use.

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

A workaround, if that's what you need, would be to add a constrain (see this) to the action in the earlier to a set of values i.e. List, View. Of course that with these types of rules, you can't have a controller with the same name of an action.

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

Also remember that if you specify a default action & id in the "{action}/{id}"rule, that will be used when you hit the route of your site.

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

回答by Haacked

Why does the first URL in the new list still have "Customer". I assume that's a typo and you meant:

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

The following routes work for me:

以下路线对我有用:

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 = "" }
);

How are you generating your links. Since the Controller is no longer in the URL of your route (aka, you don't have "{controller}" in the route URL), but it's a default value, you need to make sure to specify the controller when generating routes.

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

Thus instead of

因此,而不是

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

do

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

Why? Suppose you had the following routes.

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

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

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

Which route did you mean when you call this?

你打电话时指的是哪条路线?

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

You can differentiate by specifying the controller and we'll pick the one that has a default value that matches the specified one.

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

回答by SLaks

You can create a route that is constrained to only match actions in your Customercontroller.

您可以创建一个仅限于匹配Customer控制器中的操作的路由

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
}