Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone recreate the following bug I'm experiencing in routes with 2 or more optional parameters, in .NET 4.0 / MVC3?

I know that if you think you found a bug in the .NET framework you are most likely wrong, but that's why I'm writing this question, so please hear me out.

I am fairly certain that there is a difference between the routes in .NET 3.5 and .NET 4.0 when it comes to optional parameters. Specifically if you have more than one optional parameter in your route. I have not been able to find this breaking change noted in any release notes of either .NET 4.0 or MVC 3 so thats why I call it a bug.

EDIT: This bug only manifests itself when you try to build a route url using code like the url or html helpers in mvc. If you actually request the url in a browser, in an actual mvc app, it works just fine. So if my test app below were an actual mvc app there wouldn't be a problem if you tried to request '/root/test1'.

What I need you to do is run the following test program, it should be fairly self-explanatory, but basically it just sets up a route with some optional paramters.

  1. Create a new .NET 4 console application

  2. Change the target framework to '.NET Framework 4' instead of '.NET Framework 4 Client Profile'

  3. Add references to:
    System.Web 4.0
    System.Web.Routing 4.0
    System.Web.Mvc 3.0

  4. Paste following code to the program.cs file, overwriting any previous content:

    using System;
    using System.IO;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    public class Program
    {
        static void Main()
        {
            var httpCtx = new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://localhost/", null), new HttpResponse(new StringWriter())));
    
            var routes = RouteTable.Routes;
            routes.MapRoute("Test", "root/{test1}/{test2}/{test3}", new { test2 = UrlParameter.Optional, test3 = UrlParameter.Optional });
    
            var context = new RequestContext(httpCtx , new RouteData());
    
            var url = new UrlHelper(context);
    
            var expected1 = "/root/test1";
            var expected2 = "/root/test1/test2";
            var expected3 = "/root/test1/test2/test3";
    
            var actual1 = url.RouteUrl("Test", new { test1 = "test1" });
            var actual2 = url.RouteUrl("Test", new { test1 = "test1", test2 = "test2" });
            var actual3 = url.RouteUrl("Test", new { test1 = "test1", test2 = "test2", test3 = "test3" });
    
            var result1 = actual1 == expected1;
            var result2 = actual2 == expected2;
            var result3 = actual3 == expected3;
    
            Console.WriteLine("Test 1: {0} ({1})", result1 ? "Success" : "Fail", result1 ? string.Format("'{0}'", actual1) : string.Format("Expected '{0}' but was '{1}'", expected1, actual1 ?? "<null>"));
            Console.WriteLine("Test 2: {0} ({1})", result2 ? "Success" : "Fail", result2 ? string.Format("'{0}'", actual2) : string.Format("Expected '{0}' but was '{1}'", expected2, actual2 ?? "<null>"));
            Console.WriteLine("Test 3: {0} ({1})", result3 ? "Success" : "Fail", result3 ? string.Format("'{0}'", actual3) : string.Format("Expected '{0}' but was '{1}'", expected3, actual3 ?? "<null>" ));
            Console.ReadLine();
        }
    }
    
  5. Run program, and note the resulting urls.
    On my machine the result is:

    Test 1: Fail    (expected '/root/test1' but was '<null>')
    Test 2: Success ('/root/test1/test2')
    Test 3: Success ('/root/test1/test2/test3')
    
  6. Now change the target framework to .NET 3.5 add references to:
    System.Web.Mvc 2.0
    System.Web.Routing 3.5
    System.Web.Abstrations 3.5

  7. Run the program again and see the 3 test cases all succeed.
    This time the result is:

    Test 1: Success ('/root/test1')  
    Test 2: Success ('/root/test1/test2')  
    Test 3: Success ('/root/test1/test2/test3')
    

So it seems to me there is a bug in the .NET 4 or MVC 3. If you find the same issue please vote on the following issue in connect: https://connect.microsoft.com/VisualStudio/feedback/details/630568/url-routing-with-two-optional-parameters-unspecified-fails-on-asp-net-mvc3-rc2#details

Feel free to test this out in a regular MVC application if you think theres something wrong in the test program.

like image 217
JohannesH Avatar asked Jan 30 '11 22:01

JohannesH


2 Answers

So, Phil Haack just released a blog post detailing that this is a known issue and will be fixed in the next release of the .NET framework, or maybe if we are lucky as a bug fix to the particular dll. I'm not exactly sure how they will fix it though. Whether they will outlaw more than 1 optional parameter, or whether they will resolve it in the manner I've outlined in the comments to Darin's answer. At least they know about it and can take steps to specify the expected behavior with more than one optional parameter more in the future.

like image 200
JohannesH Avatar answered Nov 03 '22 05:11

JohannesH


Only the last parameter of a route definition can be optional and this rule has been enforced in ASP.NET MVC 3. In your example you have both test2 and test3 optional which is not possible. Consider the following URL:

root/test1/test2

Given this URL the routing engine cannot say whether test2="test2" and test3="" or test2="" and test3="test2". So why generating an URL which can never be parsed back to it's constituent tokens?

Now maybe that's a bug or a feature or call it whatever but in all cases you should avoid having such routes. IMHO the bug here is that the framework doesn't throw an exception telling you that only the last parameter in a route definition can be optional, but of course that's just my opinion.

like image 28
Darin Dimitrov Avatar answered Nov 03 '22 04:11

Darin Dimitrov