Why is ASP.NET-MVC Routing's UrlParameter.Optional ignored when using this Regex?
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开发者_JS百科/ 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.
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>
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();
}
精彩评论