2

When adding custom routing constraints to my route parameters I am finding that it is breaking the Url.Action method I use to build my links. If the route constraint is simply a regular expression then the Url.Action method continues to recognize the parameter, however if it is a custom constraint which I define, Url.Action method gives my parameter as a request parameter.

Here is my route definition:

routes.MapRoute(
            "Event",
            "Events/{strDate}",
            new { controller = "Events", action = "Index", strDate = DateTime.Today.ToString("yyyy-MM-dd") },
            new { strDate = new IsValidDateConstraint() },
            new[] { "MyProject.Controllers" }
        );

routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional },
            new[] { "MyProject.Controllers" }
        );

The IsValidDateConstraint class inherits from IRouteConstraint and returns true or false if the strDate parameter parses correctly to a DateTime object:

public class IsValidDateConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (routeDirection == RouteDirection.IncomingRequest)
        {
            DateTime dt = new DateTime();

            if (DateTime.TryParse(values["strDate"].ToString(), out dt))
                return true;
        }

        return false;
    }
}

Using the Url.Action method to build URL's:

@Url.Action("Index", "Events", new { strDate = ViewBag.CurrentDate.AddDays(1).ToString("yyyy-MM-dd") })

The resulting link is: /Events?strDate=2012-08-15

Everything routes correctly if I type in /Events/2012-08-15, it's just that the Url.Action method is not recognizing that strDate is a parameter defined in my route only when I apply my custom routing constraint. If I comment out the custom routing constraint then the Url.Action method maps the URL correctly.

Any ideas on why the Url.Action is not recognizing my route parameter when I have a custom route constraint defined?

1 Answers1

4

You haven't shown how your IsValidDateConstraint looks like but make sure you are doing a culture invariant parsing for the yyyy-MM-dd format:

public class IsValidDateConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        DateTime date;
        return DateTime.TryParseExact(
            values[parameterName] as string, 
            "yyyy-MM-dd", 
            CultureInfo.InvariantCulture, 
            DateTimeStyles.None, 
            out date
        );
    }
}

Also make sure that this route is placed before the default route:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Event",
        "Events/{strDate}",
        new { controller = "Events", action = "Index", strDate = DateTime.Today.ToString("yyyy-MM-dd") },
        new { strDate = new IsValidDateConstraint() },
        new[] { "MyProject.Controllers" }
    );

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

also DateTime.Parse(ViewBag.CurrentDate.ToString()) looks a wee-bit of a WTFkish code. If ViewBag.CurrentDate is already a DateTime you could directly write:

@Url.Action(
    "Index", 
    "Events", 
    new { 
        strDate = ViewBag.CurrentDate.AddDays(1).ToString("yyyy-MM-dd") 
    }
)

Obviously a much better solution is to use view models:

@Url.Action(
    "Index", 
    "Events", 
    new { 
        strDate = Model.CurrentDate.AddDays(1).ToString("yyyy-MM-dd") 
    }
)

UPDATE:

Now that you have shown your code the problem comes from the if condition you have put in your constraint:

if (routeDirection == RouteDirection.IncomingRequest)

When using the Url.Action helper this condition is never satisfied. Only when resolving an incoming url. So you will have to remove it if you want this constraint to work with url helpers.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I've updated my post to include all of the code I use. Keep in mind that this problem is happening for EVERY route constraint I set, not just the ones checking for a valid DateTime string. As for the ViewBag you are right, not sure why I did the double parsing there, but as this is the ONLY variable I need I found it easier to not create a ViewModel. This information is very informative and precise, but the problem is still persisting. Any other thoughts? – Christopher.Cubells Aug 14 '12 at 17:29
  • In your constraint you are using `if (routeDirection == RouteDirection.IncomingRequest)`. That's why your code doesn't work. It always returns false when evaluated by the Url.Action helper because that's not an incoming request. Look at the code I have shown in my answer. There's no such `if` condition in my constraint. I have updated my answer to include this information at the end. – Darin Dimitrov Aug 14 '12 at 17:34
  • Makes perfect sense, I appreciate the help. Thank you! – Christopher.Cubells Aug 14 '12 at 17:53