asp.net-mvc ASP.NET MVC:带有可选参数的路由,但如果提供,则必须匹配 \d+
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3862336/
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: Route with optional parameter, but if supplied, must match \d+
提问by Deniz Dogan
I'm trying to write a route with a nullable int in it. It should be possible to go to both /profile/
but also /profile/\d+
.
我正在尝试编写一个包含可为空 int 的路由。应该可以去两者/profile/
而且/profile/\d+
。
routes.MapRoute("ProfileDetails", "profile/{userId}",
new {controller = "Profile",
action = "Details",
userId = UrlParameter.Optional},
new {userId = @"\d+"});
As you can see, I say that userId
is optional but also that it should match the regular expression \d+
. This does not work and I see why.
如您所见,我说这userId
是可选的,但它应该与正则表达式匹配\d+
。这不起作用,我明白为什么。
But how would I construct a route that matches just /profile/
but also /profile/
followed by a number?
但是,我将如何构建一条既匹配/profile/
又/profile/
后跟数字的路线?
回答by Mark Bell
The simplestway would be to just add another route without the userId
parameter, so you have a fallback:
在最简单的方法是只需添加另一条路线没有userId
参数,让你有一个备用的:
routes.MapRoute("ProfileDetails", "profile/{userId}",
new {controller = "Profile",
action = "Details",
userId = UrlParameter.Optional},
new {userId = @"\d+"});
routes.MapRoute("Profile", "profile",
new {controller = "Profile",
action = "Details"});
As far as I know, the only other way you can do this would be with a custom constraint. So your route would become:
据我所知,您可以执行此操作的唯一其他方法是使用自定义约束。所以你的路线会变成:
routes.MapRoute("ProfileDetails", "profile/{userId}",
new {controller = "Profile",
action = "Details",
userId = UrlParameter.Optional},
new {userId = new NullableConstraint());
And the custom constraint code will look like this:
自定义约束代码将如下所示:
using System;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
namespace YourNamespace
{
public class NullableConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest && parameterName == "userId")
{
// If the userId param is empty (weird way of checking, I know)
if (values["userId"] == UrlParameter.Optional)
return true;
// If the userId param is an int
int id;
if (Int32.TryParse(values["userId"].ToString(), out id))
return true;
}
return false;
}
}
}
I don't know that NullableConstraint
is the best name here, but that's up to you!
我不知道这NullableConstraint
是这里最好的名字,但这取决于你!
回答by Chev
It's possible something changed since this question was answered but I was able to change this:
自从回答了这个问题后,可能会发生一些变化,但我能够改变这一点:
routes.MapPageRoute(
null,
"projects/{operation}/{id}",
"~/Projects/ProjectWizard.aspx",
true,
new RouteValueDictionary(new
{
operation = "new",
id = UrlParameter.Optional
}),
new RouteValueDictionary(new
{
id = new NullableExpressionConstraint(@"\d+")
})
);
With this:
有了这个:
routes.MapPageRoute(
null,
"projects/{operation}/{id}",
"~/Projects/ProjectWizard.aspx",
true,
new RouteValueDictionary(new
{
operation = "new",
id = UrlParameter.Optional
}),
new RouteValueDictionary(new
{
id = @"\d*"
})
);
Simply using the *
instead of the +
in the regular expression accomplished the same task. The route still fired if the parameter was not included, but if included it would only fire if the value was a valid integer. Otherwise it would fail.
只需在正则表达式中使用 the*
而不是 the 即可+
完成相同的任务。如果不包含参数,路由仍然会触发,但如果包含参数,它只会在值为有效整数时触发。否则就会失败。
回答by jordanbtucker
ASP.NET MVC 3 has solved this problem, and as Alex Ford brought out, you can use \d*
instead of writing a custom constraint. If your pattern is more complicated, like looking for a year with \d{4}
, just make sure your pattern matches what you want as well as an empty string, like (\d{4})?
or \d{4}|^$
. Whatever makes you happy.
ASP.NET MVC 3 解决了这个问题,正如Alex Ford 提出的,您可以使用\d*
而不是编写自定义约束。如果您的模式更复杂,例如使用 查找年份\d{4}
,只需确保您的模式匹配您想要的内容以及空字符串,例如(\d{4})?
或\d{4}|^$
。什么都让你开心。
If you are still using ASP.NET MVC 2 and want to use Mark Bell's exampleor NYCChris' example, please be aware that the route will match as long as the URL parameter containsa match to your pattern. This means that the pattern \d+
will match parameters like abc123def
. To avoid this, wrap the pattern with ^(
and )$
either when defining your routes or in the custom constraint. (If you look at System.Web.Routing.Route.ProcessConstraintin Reflector, you'll see that it does this for you when using the built in constraint. It also sets the CultureInvariant, Compiled, and IgnoreCaseoptions.)
如果您仍在使用 ASP.NET MVC 2 并且想要使用Mark Bell 的示例或NYCChris 的示例,请注意只要 URL 参数包含与您的模式匹配的路由就会匹配。这意味着该模式\d+
将匹配像abc123def
. 为避免这种情况,请在定义路由时或在自定义约束中使用^(
和包装模式)$
。(如果你看一下System.Web.Routing.Route.ProcessConstraint的反射,你会看到它这样做对您在使用内置的约束,同时也设置了CultureInvariant,编译和IGNORECASE选项。)
Since I already wrote my own custom constraint with the default behavior mentioned above before realizing I didn't have to use it, I'll leave it here:
由于在意识到不必使用它之前,我已经使用上面提到的默认行为编写了自己的自定义约束,因此我将其保留在此处:
public class OptionalConstraint : IRouteConstraint
{
public OptionalConstraint(Regex regex)
{
this.Regex = regex;
}
public OptionalConstraint(string pattern) :
this(new Regex("^(" + pattern + ")$",
RegexOptions.CultureInvariant |
RegexOptions.Compiled |
RegexOptions.IgnoreCase)) { }
public Regex Regex { get; set; }
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
if(routeDirection == RouteDirection.IncomingRequest)
{
object value = values[parameterName];
if(value == UrlParameter.Optional)
return true;
if(this.Regex.IsMatch(value.ToString()))
return true;
}
return false;
}
}
And here's an example route:
这是一个示例路线:
routes.MapRoute("PostsByDate",
"{year}/{month}",
new { controller = "Posts",
action = "ByDate",
month = UrlParameter.Optional },
new { year = @"\d{4}",
month = new OptionalConstraint(@"\d\d") });
回答by Anthony Johnston
should your regex be \d*?
你的正则表达式应该是 \d* 吗?
回答by NYCChris
Thanks to Mark Bell for this answer, it helped me quite a bit.
感谢 Mark Bell 的回答,对我帮助很大。
I'm wondering why you hard coded the check for "userId" in the constraint? I slightly rewrote your class like to user the parameterName
parameter, and it seems to be working just fine.
我想知道为什么你在约束中硬编码了“userId”的检查?我稍微重写了你的类,就像使用parameterName
参数一样,它似乎工作得很好。
Am I missing anything by doing it this way?
这样做我错过了什么吗?
public class OptionalRegExConstraint : IRouteConstraint
{
private readonly Regex _regEx;
public OptionalRegExConstraint(string matchExpression=null)
{
if (!string.IsNullOrEmpty(matchExpression))
_regEx = new Regex(matchExpression);
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest)
{
if (values[parameterName] == UrlParameter.Optional) return true;
return _regEx != null && _regEx.Match(values[parameterName].ToString()).Success;
}
return false;
}
}
回答by SomeShinyObject
I needed to validate a few things with more than just a RegEx but was still getting an issue similar to this. My approach was to write a constraint wrapper for any custom route constraints I may already have:
我需要使用的不仅仅是 RegEx 来验证一些事情,但仍然遇到与此类似的问题。我的方法是为我可能已经拥有的任何自定义路由约束编写一个约束包装器:
public class OptionalRouteConstraint : IRouteConstraint
{
public IRouteConstraint Constraint { get; set; }
public bool Match
(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection
)
{
var value = values[parameterName];
if (value != UrlParameter.Optional)
{
return Constraint.Match(httpContext, route, parameterName, values, routeDirection);
}
else
{
return true;
}
}
}
And then, in constraints
under a route in RouteConfig.cs
, it would look like this:
然后,在 inconstraints
的路径下RouteConfig.cs
,它看起来像这样:
defaults: new {
//... other params
userid = UrlParameter.Optional
}
constraints: new
{
//... other constraints
userid = new OptionalRouteConstraint { Constraint = new UserIdConstraint() }
}