Подтвердить что ты не робот

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/, за которым следует число?

4b9b3361

Ответ 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;
    }
}