20

Is there as easy way to convert string URL to RouteValueDictionary collection? Some method like UrlToRouteValueDictionary(string url).

I need such method because I want to 'parse' URL according to my routes settings, modify some route values and using urlHelper.RouteUrl() generate string URL according to modified RouteValueDictionary collection.

Thanks.

DaveRandom
  • 87,921
  • 11
  • 154
  • 174
Roman
  • 4,531
  • 10
  • 40
  • 69

4 Answers4

39

Here is a solution that doesn't require mocking:

var request = new HttpRequest(null, "http://localhost:3333/Home/About", "testvalue=1");
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var values = routeData.Values;
// The following should be true for initial version of mvc app.
values["controller"] == "Home"
values["action"] == "Index"

Hope this helps.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Piotr Czapla
  • 25,734
  • 24
  • 99
  • 122
  • @david.s I had problems with the solution you posted. It couldn't sepearate a route value from querystring. Piotr's solution works fine. – Ufuk Hacıoğulları Sep 11 '13 at 08:12
  • +10! Is there a way to get this into the request.RequestContext.RouteData? I have an extension method that acts on RequestContext.RouteData, but it doesn't match routeData above. – xr280xr Sep 15 '15 at 18:08
  • @Piotr - thanks. This code works and allows me to do some routing based on the Referring URL - mission accomplished! However, I can only get the code to run in the context of the application, but, never in the context of my Visual Studio unit test. I'm populating the RouteTable from the RouteConfig, but, still, I get a NullReferenceException when the GetRouteData method is called. – bkwdesign Dec 21 '15 at 21:19
3

You would need to create a mocked HttpContext as routes constrains requires it.

Here is an example that I use to unit test my routes (it was copied from Pro ASP.Net MVC framework):

        RouteCollection routeConfig = new RouteCollection();
        MvcApplication.RegisterRoutes(routeConfig);
        var mockHttpContext = new MockedHttpContext(url);
        RouteData routeData = routeConfig.GetRouteData(mockHttpContext.Object);
        // routeData.Values is an instance of RouteValueDictionary
        //...
Piotr Czapla
  • 25,734
  • 24
  • 99
  • 122
  • Thanks, this solution looks interesting. But its a pity there is no 'lightweight' method that does not require mocking, because I use Mocks in my unit tests but not in general code. Looks like I should. – Roman Nov 04 '09 at 11:02
  • I've had similar concerns and finally decided to pass serialized RouteCollection between requests. – Piotr Czapla Nov 05 '09 at 15:36
1

I wouldn't rely on RouteTable.Routes.GetRouteData from previous examples because in that case you risk missing some values (for example if your query string parameters don't fully fit any of registered mapping routes). Also, my version doesn't require mocking a bunch of request/response/context objects.

public static RouteValueDictionary UrlToRouteValueDictionary(string url)
{
    int queryPos = url.IndexOf('?');

    if (queryPos != -1)
    {
        string queryString = url.Substring(queryPos + 1);
        var valuesCollection = HttpUtility.ParseQueryString(queryString);
        return new RouteValueDictionary(valuesCollection.AllKeys.ToDictionary(k => k, k => (object)valuesCollection[k]));
    }

    return new RouteValueDictionary();
}
Serg
  • 6,742
  • 4
  • 36
  • 54
0

Here is a solution that doesn't require instantiating a bunch of new classes.

var httpContext = context.Get<System.Web.HttpContextWrapper>("System.Web.HttpContextBase");
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(httpContext);
var values = routeData.Values;
var controller = values["controller"];
var action = values["action"];

The owin context contains an environment that includes the HttpContext.

Steve Hiner
  • 2,523
  • 3
  • 24
  • 33
  • can you explain your owin context a little more? Are you saying your snippet works if we reference the Microsoft.Owin libraries? – bkwdesign Dec 21 '15 at 21:27
  • I'm running the code above in an MVC project that uses OWIN. MVC 5 uses OWIN but earlier versions didn't. If you are using MVC 5 or later then you shouldn't need to add any references. – Steve Hiner Dec 22 '15 at 22:30