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
ASP.NET MVC Routing with Default Controller
提问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 仍然有“客户”。我认为这是一个错字,你的意思是:
- 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
- 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
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
}

