4

I'd like to handle URLs like this:

/Id/Key1/Value1/Key2/Value2/Key3/Value3/

Right now, I have set up a rule like this:

/{id}/{*parameters}

The parameters object is passed as a single string to all the actions that are involved in forming the response. This does work, but I have a few problems with it:

  1. Each action must resolve the string for itself. I've, of course, made an extension method that turns the string to a Dictionary<string, string>, but I'd prefer it if the dispatching mechanism gave my methods a Dictionary<string, string> directly - or, better yet, the actual pairs as separate arguments.
  2. Action links will still add parameters using the traditional format (?Key1=Value1). I guess I could write specialized helpers with my desired format, but I'd prefer it if there was a way to make the existing overloads follow the above routing rule.

Is there a way to do the above?

Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104

2 Answers2

4

You could write a custom route:

public class MyRoute : Route
{
    public MyRoute()
        : base(
            "{controller}/{action}/id/{*parameters}",
            new MvcRouteHandler()
        )
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var rd = base.GetRouteData(httpContext);
        if (rd == null)
        {
            return null;
        }

        string parameters = rd.GetRequiredString("parameters");
        IDictionary<string, string> parsedParameters = YourExtensionMethodThatYouAlreadyWrote(parameters);
        rd.Values["parameters"] = parsedParameters;
        return rd;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        object parameters;
        if (values.TryGetValue("parameters", out parameters))
        {
            var routeParameters = parameters as IDictionary<string, object>;
            if (routeParameters != null)
            {
                string result = string.Join(
                    "/", 
                    routeParameters.Select(x => string.Concat(x.Key, "/", x.Value))
                );
                values["parameters"] = result;
            }
        }
        return base.GetVirtualPath(requestContext, values);
    }
}

which could be registered like that:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add("my-route", new MyRoute());

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

and now your controller actions could take the following parameters:

public ActionResult SomeAction(IDictionary<string, string> parameters)
{
    ...
}

As far as generating links following this pattern is concerned, it's as simple as:

@Html.RouteLink(
    "Go", 
    "my-route", 
    new {
        controller = "Foo",
        action = "Bar",
        parameters = new RouteValueDictionary(new { 
            key1 = "value1", 
            key2 = "value2",
        }) 
    }
)

or if you wanted a <form>:

@using (Html.BeginRouteForm("my-route", new { controller = "Foo", action = "Bar", parameters = new RouteValueDictionary(new { key1 = "value1", key2 = "value2" }) }))
{
    ...    
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    The solution is a working one but in my opinion it's violating the single responsible principle in a really bad way. – Peter Kiss Jun 23 '13 at 16:14
  • @PeterKiss, care to elaborate? Is it because of the way I suggested to generate action links? By writing custom extension methods? I have updated my answer to illustrate how you could use the base helpers to generate links to this custom route. – Darin Dimitrov Jun 23 '13 at 16:22
  • Very informative answer - thanks to it, I now understand the routing mechanism a lot better. The code works perfectly and it made it very easy from there on to pass the parameters as individual named arguments to the methods. – Theodoros Chatzigiannakis Jun 23 '13 at 17:10
1

Write your own model binder for a specialized dictionary. If you will have one there will be no need for parsing the string in each action method.

Peter Kiss
  • 9,309
  • 2
  • 23
  • 38