更改路由
只需为其提供路由并将id
默认值删除为可选参数:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index" } // defaults
);
但这些默认值永远不会被使用,所以:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}" // URL with parameters
);
这是需要定义的实际路线。
你的controller
and action
are 永远不会根据需要从 URL 中省略,id
并且是 URL 定义中的最后一个,这意味着第一对也必须在那里。
我不确定这是否正是您所需要的,但是根据您问题的当前状态,这应该可以为您解决问题。但是如果你需要你的 id 来获得一些预定义的值,你可以在你的路由定义中给它一个不同的值:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = 1 } // defaults
);
这使得所有三个都可以从 URL 中省略,并且它们都将具有特定的值。
id
定义格式的路由约束
您还可以使用路由约束来告诉路由 URL 参数的外观。由于您id
似乎必须是数字,这也是一种可能性:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index" }, // defaults
new { id = "\d+" } // constraints
);
编辑后
您的问题实际上有两种解决方案。
- 正确路由以定义
id
某些路由所需
- 动作方法选择器过滤器以声明方式标记它们需要某些参数的动作
解决方案 1:路由
这一个定义了几个路由定义,但硬编码需要id
参数的操作:
routes.MapRoute(
"RequiresId",
"{controller}/{action}/{id}", // URL with parameters
null,
new { action = "Detail" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}" // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { action = "(?!Detail).+" } // any action except "Detail"
);
第一条路线定义了所有具有操作方法的控制器Detail
都需要id
参数。这很简单,只要所有具有这些操作的控制器都具有相同的要求(可能是您的情况)。但是如果这不是真的,它会变得更加复杂,因为您必须为每个控制器提供约束。
解决方案 2:操作方法选择器过滤器
此解决方案仅需要带有可选的默认路由id
。自定义操作方法选择器过滤器(鲜为人知且很少使用)将帮助您编写如下代码:
[RequiresRouteValues("id, name")]
public ActionResult Detail(int id, string name)
{
...
}
你会把它放在那些需要它的方法上。如果该特定参数不存在,控制器动作调用程序将无法找到合适的方法,因此返回 404。
我已经在我的博客上详细讨论过这个问题。它还包括过滤器的代码,如下所示:
/// <summary>
/// Represents an attribute that is used to restrict action method selection based on route values.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class RequiresRouteValuesAttribute : ActionMethodSelectorAttribute
{
#region Properties
/// <summary>
/// Gets required route value names.
/// </summary>
public ReadOnlyCollection<string> Names { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether to include form fields in the check.
/// </summary>
/// <value><c>true</c> if form fields should be included; otherwise, <c>false</c>.</value>
public bool IncludeFormFields { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to include query variables in the check.
/// </summary>
/// <value>
/// <c>true</c> if query variables should be included; otherwise, <c>false</c>.
/// </value>
public bool IncludeQueryVariables { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
/// </summary>
private RequiresRouteValuesAttribute()
{
this.IncludeFormFields = true;
this.IncludeQueryVariables = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
/// </summary>
/// <param name="commaSeparatedNames">Comma separated required route values names.</param>
public RequiresRouteValuesAttribute(string commaSeparatedNames)
: this((commaSeparatedNames ?? string.Empty).Split(','))
{
// does nothing
}
/// <summary>
/// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
/// </summary>
/// <param name="names">Required route value names.</param>
public RequiresRouteValuesAttribute(IEnumerable<string> names)
: this()
{
if (names == null || names.Count().Equals(0))
{
throw new ArgumentNullException("names");
}
// store names
this.Names = new ReadOnlyCollection<string>(names.Select(val => val.Trim()).ToList());
}
#endregion
#region ActionMethodSelectorAttribute implementation
/// <summary>
/// Determines whether the action method selection is valid for the specified controller context.
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="methodInfo">Information about the action method.</param>
/// <returns>
/// true if the action method selection is valid for the specified controller context; otherwise, false.
/// </returns>
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
// always include route values
HashSet<string> uniques = new HashSet<string>(controllerContext.RouteData.Values.Keys);
// include form fields if required
if (this.IncludeFormFields)
{
uniques.UnionWith(controllerContext.HttpContext.Request.Form.AllKeys);
}
// include query string variables if required
if (this.IncludeQueryVariables)
{
uniques.UnionWith(controllerContext.HttpContext.Request.QueryString.AllKeys);
}
// determine whether all route values are present
return this.Names.All(val => uniques.Contains(val));
}
#endregion
}
第一个在具有多个控制器和与之相关的不同约束的应用程序中使事情变得复杂。第二个是优雅的,适用于简单和复杂的场景。
我当然会选择解决方案 2。但在这种情况下,把我当作一个有偏见的开发人员。