Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customize route to map url dynamically to a composed named controller in ASP.NET MVC3

I need to map URLs like this:

/stock/risk -->StockRiskController.Index()

/stock/risk/attr -->StockRiskController.Attr()

/srock/risk/chart -->StockRiskController.Chart()

...

/bond/performance -->BondPerformanceController.Index()

/bond/performance/attr -->BondPerformanceController.Attr()

/bond/performance/chart -->BondPerformanceController.Chart() ...

The first part is dynamic but enumerable, the second part has only two options(risk|performance).

For now I know only two ways:

  1. customized a ControllerFactory(seems overkilled or complicated)
  2. hard code all the combinations because they are enumerable(ugly).

Can I use routes.MapRoute to achieve this? Or any other handy way?

like image 754
Chris Li Avatar asked Dec 10 '25 01:12

Chris Li


1 Answers

There is a nice solution based on IRouteConstraint. First of all we have to create new route mapping:

routes.MapRoute(
  name: "PrefixedMap",
  url: "{prefix}/{body}/{action}/{id}",
  defaults: new { prefix = string.Empty, body = string.Empty
                    , action = "Index", id = string.Empty },
  constraints: new { lang = new MyRouteConstraint() }
);

Next step is to create our Constraint. Before I will introduce some way how to check relevance as mentioned above - two list with possible values, but logic could be adjusted

public class MyRouteConstraint : IRouteConstraint
{
  public readonly IList<string> ControllerPrefixes = new List<string> { "stock", "bond" };
  public readonly IList<string> ControllerBodies = new List<string> { "risk", "performance" };
  ...

And now the Match method, which will adjust the routing as we need

public bool Match(System.Web.HttpContextBase httpContext
      , Route route, string parameterName, RouteValueDictionary values
      , RouteDirection routeDirection)
{
    // for now skip the Url generation
    if (routeDirection.Equals(RouteDirection.UrlGeneration))
    {
        return false;
    }

    // try to find out our parameters
    string prefix = values["prefix"].ToString();
    string body = values["body"].ToString();

    var arePartsKnown =
        ControllerPrefixes.Contains(prefix, StringComparer.InvariantCultureIgnoreCase) &&
        ControllerBodies.Contains(body, StringComparer.InvariantCultureIgnoreCase);

    // not our case
    if (!arePartsKnown)
    {
        return false;
    }

    // change controller value
    values["controller"] = prefix + body;
    values.Remove("prefix");
    values.Remove("body");

    return true;
}

You can play with this method more, but the concept should be clear now.

NOTE: I like your approach. Sometimes it is simply much more important to extend/adjust routing then go to code and "fix names". Similar solution was working here: Dynamically modify RouteValueDictionary

like image 84
Radim Köhler Avatar answered Dec 12 '25 13:12

Radim Köhler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!