Regex – Why is ASP.NET-MVC Routing’s UrlParameter.Optional ignored when using this Regex

asp.net-mvcasp.net-mvc-2regexroutingurl-routing

This is a stripped down example of a problem I was having this morning with ASP.NET MVC's URL routing.

Fairly simple, I wanted a route's Action to be called, whether or not the parameter on the end was supplied.

This route works fine, matching both /apple/ and /apple/test/

routes.MapRoute(
    "Working Route",
    "apple/{parameter}",
    new { 
        controller = "Apple", 
        action = "Action", 
        parameter = UrlParameter.Optional
    },
    new { parameter = @"([a-z0-9\.-]+)" }
);

However, this second route will only match /banana/test/ and the like. When I try /banana/, the router just passes right over it and returns the catch-all 404 error.

routes.MapRoute(
    "Non-Working Route",
    "banana/{parameter}",
    new { 
        controller = "Banana", 
        action = "Action", 
        parameter = UrlParameter.Optional
    },
    new { parameter = @"([a-z0-9]+)" }
);

The only difference is the Regex validation of the parameter, but as it's quite a simple Regex match, they should both work perfectly fine for a URL like /banana/, yet the second route just fails to recognise it.

I side-stepped my problem by just changing the Regex on route #2 to match that on route #1, and accept the '.' and '-' characters, I just wondered if anyone knows why this seems to be happening.

EDIT:

Here are the Controllers and Actions that I'm using for my example. Nothing fancy here.

public class AppleController : Controller
{
    public ActionResult Action(string parameter)
    {
        if (parameter == null)
        {
            parameter = "No parameter specified.";
        }
        ViewData["parameter"] = parameter;
        return View();
    }
}

public class BananaController : Controller
{
    public ActionResult Action(string parameter)
    {
        if (parameter == null)
        {
            parameter = "No parameter specified.";
        }
        ViewData["parameter"] = parameter;
        return View();
    }
}

So my problem is that /apple/ would display "No parameter specified.", while /banana/ gives me an undesired 404 instead.


So far..

Using parameter = URLParameter.Optional in the Route declaration:
Route #1 works perfectly, Route #2 doesn't match without the parameter.

Using parameter = "" in the Route declaration:
Both Route #1 & Route #2 fail to match when the parameter is left off the URL.

Declaring parameter = "" in the Action method signature:
Not possible due to .NET version.

Removing all other routes has no effect.

Best Answer

If the token is optional then whatever regex constraint you use must also reflect that, e.g. (foo)?.


I was able to reproduce this issue with ASP.NET MVC 2 on .NET 4 . Then I upgraded to ASP.NET MVC 3 and everything worked as expected. The solution I present above does not work with ASP.NET MVC 2, but it works with ASP.NET MVC 3, so I can only assume this is a bug on v2 that is now fixed in v3.

You can reference v2, and use this on web.config to test with v3:

<runtime>
   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
         <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
         <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
   </assemblyBinding>
</runtime>