5
votes

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.

2
Something else is going on. I'm reproducing your set up as best I can tell and both routes work for me, with and without the parameter. Odd.nkirkes
Just to make sure, your default route should be coming after the specified routes (most to least specific) when you register them. Can you confirm this is the case?nkirkes
My default route is a catch-everything route that is declared last. The ones above are the first two routes declared in my Global.asax.cs file.Ben Jenkinson

2 Answers

4
votes

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


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>
0
votes

2nd EDIT

Matching ~/fruit/ and ~fruit/apple

routes.MapRoute(
    "Working Route",
    "Fruit/{fruit}",
    new
    {
        controller = "Fruit",
        action = "Index",
        fruit = ""
    },
    new { fruit = @"([a-z0-9\.-]+)" }
);

public ActionResult Index(string fruit)
{
    ViewData["fruit"] = !String.IsNullOrEmpty(fruit) ? fruit : "None specified.";
    return View();
}

Matching ~/banana/ and ~/banana/yellow

routes.MapRoute(
    "Non-Working Route",
    "Banana/{color}",
    new
    {
        controller = "Banana",
        action = "Index",
        color = ""
    },
    new { color = @"([a-z0-9]+)" }
);

public ActionResult Index(string color)
    {
        ViewData["Color"] = color;
        return View();
    }

1st EDIT

Try actually setting the parameter as optional on the action signature, such that: string parameter = ""


I'm not getting that behavior. Both routes work for me as you're describing them (the second route throws a 404 if I add a "-" to the route, but that's expected). What does your action method signature look like?

I set my route (and controller/view) to look like:

routes.MapRoute(
    "Working Route",
    "Fruit/{fruit}",
    new
    {
        controller = "Fruit",
        action = "Index",
        fruit = UrlParameter.Optional
    },
    new { fruit = @"([a-z0-9]+)" }
);

And my Index() action looks like:

public ActionResult Index(string fruit = "")
{
    ViewData["fruit"] = fruit;
    return View();
}