ASP.NET MVC: маршрут с дополнительным параметром, но если он указан, должен соответствовать \d +
Я пытаюсь написать маршрут с нулевым int в нем. Должно быть возможно перейти как к /profile/
, так и к /profile/\d+
.
routes.MapRoute("ProfileDetails", "profile/{userId}",
new {controller = "Profile",
action = "Details",
userId = UrlParameter.Optional},
new {userId = @"\d+"});
Как вы можете видеть, я говорю, что userId
является необязательным, но также должен соответствовать регулярному выражению \d+
. Это не работает, и я понимаю, почему.
Но как мне построить маршрут, который соответствует только /profile/
, но также /profile/
, за которым следует число?
Ответы
Ответ 1
Самый простой способ - просто добавить еще один маршрут без параметра 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"});
Насколько я знаю, единственный способ, которым вы можете это сделать, - это создать собственное ограничение. Таким образом, ваш маршрут станет следующим:
routes.MapRoute("ProfileDetails", "profile/{userId}",
new {controller = "Profile",
action = "Details",
userId = UrlParameter.Optional},
new {userId = new NullableConstraint());
И код пользовательского ограничения будет выглядеть так:
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;
}
}
}
Я не знаю, что NullableConstraint
- лучшее имя здесь, но это зависит от вас!
Ответ 2
Возможно, что-то изменилось, поскольку на этот вопрос был дан ответ, но я смог изменить это:
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+")
})
);
При этом:
routes.MapPageRoute(
null,
"projects/{operation}/{id}",
"~/Projects/ProjectWizard.aspx",
true,
new RouteValueDictionary(new
{
operation = "new",
id = UrlParameter.Optional
}),
new RouteValueDictionary(new
{
id = @"\d*"
})
);
Просто используя *
вместо +
в регулярном выражении выполняется одна и та же задача. Маршрут по-прежнему запускается, если параметр не был включен, но если он включен, он будет гореть только в том случае, если значение было действительным целым числом. В противном случае он потерпит неудачу.
Ответ 3
ASP.NET MVC 3 решил эту проблему, и, поскольку Алекс Форд вывел, вы можете использовать \d*
вместо написания пользовательского ограничения. Если ваш шаблон более сложный, например, поиск года с помощью \d{4}
, просто убедитесь, что ваш шаблон соответствует тому, что вы хотите, а также пустой строке, например (\d{4})?
или \d{4}|^$
. Что бы ни делало вас счастливым.
Если вы все еще используете ASP.NET MVC 2 и хотите использовать пример Mark Bell или пример NYCChris, помните, что маршрут будет соответствовать до тех пор, пока параметр URL содержит совпадение с вашим шаблоном. Это означает, что шаблон \d+
будет соответствовать параметрам типа abc123def
. Чтобы избежать этого, оберните шаблон с помощью ^(
и )$
либо при определении маршрутов, либо в пользовательском ограничении. (Если вы посмотрите System.Web.Routing.Route.ProcessConstraint в Reflector, вы увидите, что он делает это для вас при использовании встроенного ограничения. Он также устанавливает CultureInvariant, Compiled и IgnoreCase варианты.)
Так как я уже написал свое собственное пользовательское ограничение с поведением по умолчанию, упомянутым выше, прежде чем осознать, что мне не нужно его использовать, я оставлю его здесь:
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;
}
}
И вот пример маршрута:
routes.MapRoute("PostsByDate",
"{year}/{month}",
new { controller = "Posts",
action = "ByDate",
month = UrlParameter.Optional },
new { year = @"\d{4}",
month = new OptionalConstraint(@"\d\d") });
Ответ 4
если ваше регулярное выражение будет \d *?
Ответ 5
Спасибо Марку Белу за этот ответ, это помогло мне совсем немного.
Мне интересно, почему вы жестко закодировали чек на "userId" в ограничении? Я немного переписал ваш класс, как пользователь, параметр parameterName
, и, похоже, он работает нормально.
Я что-то пропустил, сделав так?
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;
}
}