ASP.NET MVC: Маршрут с необязательным параметром, но если он указан, должен соответствовать \ d + - PullRequest
23 голосов
/ 05 октября 2010

Я пытаюсь написать маршрут с обнуляемым int в нем.Должна быть возможность перейти как к /profile/, так и к /profile/\d+.

routes.MapRoute("ProfileDetails", "profile/{userId}",
                new {controller = "Profile",
                     action = "Details",
                     userId = UrlParameter.Optional},
                new {userId = @"\d+"});

. Как видите, я говорю, что userId является необязательным, но также и что оно должно соответствовать регулярному выражению \d+.Это не работает, и я понимаю, почему.

Но как мне построить маршрут, который соответствует только /profile/, но также /profile/, за которым следует число?

Ответы [ 6 ]

28 голосов
/ 05 октября 2010

Самый простой способ - просто добавить другой маршрут без параметра 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 - лучшее имя здесь, но это зависит от вас!

13 голосов
/ 08 мая 2012

Возможно, что-то изменилось, так как на этот вопрос был дан ответ, но я смог изменить это:

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

Простое использование * вместо + в регулярном выражении позволило выполнить ту же задачу. Маршрут все еще запускается, если параметр не был включен, но если он включен, он будет срабатывать только в том случае, если значение является допустимым целым числом. Иначе это не получится.

7 голосов
/ 11 августа 2012

ASP.NET MVC 3 решил эту проблему, и как Алекс Форд вывел , вы можете использовать \d* вместо написания пользовательского ограничения. Если ваш шаблон более сложный, например, поиск года с \d{4}, просто убедитесь, что ваш шаблон соответствует вашему желанию, а также пустой строке, такой как (\d{4})? или \d{4}|^$. Все, что делает тебя счастливым.

Если вы все еще используете ASP.NET MVC 2 и хотите использовать Пример Марка Белла или Пример 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") });
3 голосов
/ 05 октября 2010

должно ли ваше регулярное выражение быть \ d *?

2 голосов
/ 29 ноября 2011

Спасибо Марку Беллу за этот ответ, он мне очень помог.

Мне интересно, почему вы жестко закодировали проверку «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;
    }
}
0 голосов
/ 07 мая 2018

Мне нужно было проверить несколько вещей с помощью чего-то большего, чем просто 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;
        }
    }
}

А затем в constraints под маршрутом в RouteConfig.cs это будет выглядеть так:

defaults: new {
    //... other params
    userid = UrlParameter.Optional
}
constraints: new
{
    //... other constraints
    userid = new OptionalRouteConstraint { Constraint = new UserIdConstraint() }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...