1

I have a route defined like this:

 routes.MapRoute("Date", "Date/{year}/{month}/{day}", 
    new { controller = "Date", action = "Index", year = UrlParameter.Optional,
         month = UrlParameter.Optional, day = UrlParameter.Optional });

So it has 3 optional parameters, year, month and day. It works fine in routing a GET request, all the following work fine:

http://myhost/myapp/Date
http://myhost/myapp/Date/2011
http://myhost/myapp/Date/2011/8
http://myhost/myapp/Date/2011/8/17

Generating links using RouteUrl works in all cases except one.

For example, the case where I want a link including the year and a month works fine. I use the following (simplified) code in my view:

 string linkUrl = Url.RouteUrl("Date",
     new { controller = "Date", year = 2011, month = 8 },
     Request.Url.Scheme);

But the case where only the year is defined does not work and returns null, the code is:

string linkUrl = Url.RouteUrl("Date",
    new { controller = "Date", year = 2011 },
    Request.Url.Scheme);

So it all looks correct to me. Any clue what I'm doing wrong, or how to debug this further.

iandotkelly
  • 9,024
  • 8
  • 48
  • 67

1 Answers1

2

Ok, it looks like this is a known 'issue' introduced in MVC3, but due to an underlying issue in routing in .NET4, so cannot be quickly fixed by the MVC team. Although silently returning null is clearly a bug, its unclear whether the route I originally created should be allowed to work the way I wanted it too (see below).

I found this SO Question which asks for people to replicate a similar issue with two optional parameters, and an answer refers to this article too, which has a workaround.

I resolved this as per the workaround by making multiple routes, with only one optional parameter in each.

  routes.MapRoute(
    "Date-ByDay", // Route name
    "Date/{year}/{month}/{day}", // URL with parameters
    new { controller = "Date", action = "Index" } // Parameter defaults
  );

  routes.MapRoute(
    "Date-ByMonth", // Route name
    "Date/{year}/{month}", // URL with parameters
    new { controller = "Date", action = "Index", month = UrlParameter.Optional } // Parameter defaults
  );

  routes.MapRoute(
    "Date-ByYear", // Route name
    "Date/{year}", // URL with parameters
    new { controller = "Date", action = "Index", year = UrlParameter.Optional } // Parameter defaults
  );

It is arguable whether my original approach should work anyway, for example what if you miss out the optional 'month' parameter, and should the original route generate a URL of /2011/18? The 'workaround' whilst ugly, certainly is much more explicit about the behavior you want. I would argue that the expected behavior of my original route is clear (to me) and that I would have expected an exception if I had asked for a route with a year and a day, missing out the month - but I can see that this may not be everyones point of view. However this did work in MVC2.

Community
  • 1
  • 1
iandotkelly
  • 9,024
  • 8
  • 48
  • 67
  • as a possible work around, what about just taking one "date" parameter and then working out what type it is in the controller code? – iain Aug 18 '11 at 17:09
  • Date as a string? With a URL of /Date/08-18-2011? Perhaps, but I need to display a different view when the browser asks for just the year, or just the year-month. Thanks though. – iandotkelly Aug 18 '11 at 17:15
  • yeah, like that (or maybe with no spaces or slash). Then have some code to check the length to find out what it is, then build the datetime off that? – iain Aug 18 '11 at 17:16
  • That would work fine if I were looking for 'whole dates' all the time. I'm looking for if the client calls /Date to give a list of all years with data in the database, then /Date/2011 would return all months in 2011 with data. I guess I could pass 00 or 0000 if there were a missing part. That almost starts to become more complicated though, and feels a little less RESTful. – iandotkelly Aug 18 '11 at 17:43