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

Den*_*gan 23 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+"});
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我说这userId是可选的,但它应该与正则表达式匹配\d+.这不起作用,我明白为什么.

但是,我如何构建一个匹配的路由,/profile//profile/后面跟一个数字?

Mar*_*ell 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"});
Run Code Online (Sandbox Code Playgroud)

据我所知,唯一可以做到这一点的方法是使用自定义约束.所以你的路线会变成:

routes.MapRoute("ProfileDetails", "profile/{userId}",
                new {controller = "Profile",
                     action = "Details",
                     userId = UrlParameter.Optional},
                new {userId = new NullableConstraint());
Run Code Online (Sandbox Code Playgroud)

自定义约束代码如下所示:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道这NullableConstraint是最好的名字,但这取决于你!


Che*_*hev 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+")
    })
);
Run Code Online (Sandbox Code Playgroud)

有了这个:

routes.MapPageRoute(
    null,
    "projects/{operation}/{id}",
    "~/Projects/ProjectWizard.aspx",
    true,
    new RouteValueDictionary(new
    {
        operation = "new",
        id = UrlParameter.Optional
    }),
    new RouteValueDictionary(new
    {
        id = @"\d*"
    })
);
Run Code Online (Sandbox Code Playgroud)

简单地使用*而不是+在正则表达式中完成相同的任务.如果未包含参数,则仍会触发路径,但如果包含该参数,则只有在值为有效整数时才会触发.否则会失败.


jor*_*ker 7

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

如果您仍在使用ASP.NET MVC 2并希望使用Mark Bell的示例NYCChris的示例,请注意只要URL参数包含与您的模式匹配的路由,路由就会匹配.这意味着该模式\d+将匹配参数abc123def.要避免这种情况,请在定义路径时或在自定义约束中使用^(和包装模式)$.(如果你看一下System.Web.Routing.Route.ProcessConstraint反射,你会看到它这样做对您在使用内置的约束,同时也设置了CultureInvariant,编译和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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个示例路线:

routes.MapRoute("PostsByDate",
                "{year}/{month}",
                new { controller = "Posts",
                      action = "ByDate",
                      month = UrlParameter.Optional },
                new { year = @"\d{4}",
                      month = new OptionalConstraint(@"\d\d") });
Run Code Online (Sandbox Code Playgroud)