ASP.NET MVC:使用可选参数进行路由,但如果提供,则必须匹配\ d +

时间:2010-10-05 09:20:19

标签: asp.net-mvc asp.net-mvc-2

我正在尝试编写一个带有可空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 个答案:

答案 0 :(得分:28)

最简单的方式是添加另一条没有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在这里是最好的名字,但这取决于你!

答案 1 :(得分:13)

自从这个问题得到解答后,可能会发生一些变化,但我能够改变这个:

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

只需在正则表达式中使用*代替+即可完成相同的任务。如果未包含参数,则仍会触发路径,但如果包含该参数,则只有在值为有效整数时才会触发。否则就会失败。

答案 2 :(得分:7)

ASP.NET MVC 3已经解决了这个问题,而Alex Ford brought out可以使用\d*而不是编写自定义约束。如果您的模式更复杂,例如查找\d{4}的一年,只需确保您的模式符合您的要求以及空字符串,例如(\d{4})?\d{4}|^$。无论什么让你开心。

如果您仍在使用ASP.NET MVC 2并希望使用Mark Bell's exampleNYCChris' example,请注意只要URL参数包含匹配你的模式。这意味着模式\d+将匹配abc123def之类的参数。为避免这种情况,请在定义路径时或在自定义约束中使用^()$包装模式。 (如果您查看System.Web.Routing.Route.ProcessConstraint中的Reflector,则会在使用内置约束时看到它为您执行此操作。它还会设置CultureInvariant, Compiled, and 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 :(得分:3)

你的正则表达式应该是\ d *吗?

答案 4 :(得分:2)

感谢Mark Bell的回答,这对我帮助很大。

我想知道为什么你在约束中硬编码“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;
    }
}

答案 5 :(得分:0)

我需要验证一些不仅仅是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;
        }
    }
}

然后,在constraintsRouteConfig.cs的路线下defaults: new { //... other params userid = UrlParameter.Optional } constraints: new { //... other constraints userid = new OptionalRouteConstraint { Constraint = new UserIdConstraint() } } ,它看起来像这样:

position:fixed;
left:ValueFromLeft;
bottom:0;