Search code examples
asp.netasp.net-mvcasp.net-routingmaprouterenderaction

How to call controller action with a routeValue parameter in ASP.NET MVC


I have existing code with below @html.RenderAction() method:

Html.RenderAction("Widget", "Widget", new
{
    wTitle = "World map dashboard",
    wTitleSpan = "",
    wTitleDisplay = "",
    height = "300px;",
    wAction = "GetWorldMapMethod",
    wCssId = "WorldMap",
    cssOptions = "WorldMap",
    ShipSelection = "fleet",
    infoTitle = HttpUtility.HtmlEncode("<b>Info dashboard</b>"),
    infoText = HttpUtility.HtmlEncode("<p>Info</p>")
});

When this is executed, the method GetWorldMapMethod() is called. I'm trying to understand how this parameter action method is getting called.

Here's my routing configuration:

routes.MapRoute(
    "ManagementShipDetails",
    "Management/ShipDetails/{id}/{successMessage}",
    new {controller = "Management", action = "ShipDetails", successMessage = UrlParameter.Optional}
    );

routes.MapRoute(
    "Report",
    "Data/Report/{viewname}",
    new
    {
        controller = "Data",
        action = "Report",
    });

routes.MapRoute(
    "Apikeydelete",
    "Account/DeleteApiKey/{key}",
    new {controller = "Account", action = "DeleteApiKey"}
    );

routes.MapRoute(
    "FleetOverview",
    "Fleet",
    new {controller = "Data", action = "Fleet"}
    );

routes.MapRoute(
    "ShipOverview",
    "{ShipName}/Overview",
    new {controller = "Data", action = "Overview", ShipName = UrlParameter.Optional}
    );

routes.MapRoute(
    "Hull Performance",
    "{ShipName}/HullPerformanceDrop",
    new {controller = "Data", action = "HullPerformanceDrop", ShipName = UrlParameter.Optional}
    );

routes.MapRoute(
    "ReportingViewReport",
    "{ShipName}/Report/{id}",
    new
    {
        controller = "Reporting",
        action = "Report",
        ShipName = UrlParameter.Optional,
        id = UrlParameter.Optional
    }
    );

routes.MapRoute(
    "PortalDataGetValue",
    "PortalData/GetValue/{tag}/{selection}/{date}/{Filter}",
    new {controller = "PortalData", action = "GetValue", Filter = UrlParameter.Optional}
    );

routes.MapRoute(
    "PortalDataGetDashboardData",
    "PortalData/GetDashboardData/{selection}/{date}",
    new {controller = "PortalData", action = "GetDashboardData", Filter = UrlParameter.Optional}
    );

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new {controller = "Dashboards", action = "Index", id = UrlParameter.Optional} // Parameter defaults
    );   

Solution

  • TL;DR @Html.RenderAction() doesn’t rely on your routes. It will attempt to call directly into a WidgetController.Widget() action. That action—as well as any any intermediate code, such as an IControllerFactory or IActionFilter—can use your routeValues dictionary to evaluate your custom wAction route variable and respond appropriately. This could include calling into your WidgetController.GetWorldMapMethod() action.

    Critically, ASP.NET MVC is not routing to or calling your GetWorldMapMethod(); it is instead being triggered by something in your code.


    Full Answer

    As @Always_a_learner notes in the comments, without access to your controller code we can't provide a precise answer. That said, we can at least provide some background information and pair that with an educated guess as to what might be going on.

    Child Action Routing

    First, it's important to note that while @AbdulG requested your RouteConfig details, they're not really relevant here. @Html.RenderAction() requires an actionName parameter and, optionally, accepts a controllerName, thus bypassing any routes you have setup. In fact, because RenderAction() is intended to render a child action, it doesn't make sense to have a public route associated with it.

    Given that, independent of your MapRoute() configuration, we can be confident that your call to @Html.RenderAction() is going to first try to execute an action named WidgetController.Widget().

    Note: There are advanced scenarios—such as implementing a custom IControllerFactory—where this assumption might not be true, so you should confirm that there isn't e.g. a SetControllerFactory() registration in your Global.asax.cs. If there is, it could potentially intercept the request and route it to a separate action.

    Route Data Handling

    Second, the third parameter of this @Html.RenderHtml() overload represents your routeValues. So the Widget() action—as well as any intermediate code, such as an IControllerFactory, or any action filters—will have access to those values in order to implement whatever business logic they deem appropriate. This could potentially include calling into a different action.

    Given that, my assumption is that somewhere in this pipeline, there will be code that looks something like this:

    var wAction = ControllerContext.RouteData.GetRequiresString("wAction");
    
    switch (wAction) {
      case "GetWorldMapMethod":
        GetWorldMapMethod();
        break;
      default:
        break;
    }
    

    Note: This is just an example. There are dozens of different approaches for evaluating the contents of the RouteData and responding to it, including e.g. reflection. Still, if you search for wAction in your code base, that should help you isolate what code is handling that route value.

    Debugging

    If you're using an IDE like Visual Studio, the easiest solution would be to fire up a debugger, add a breakpoint to your @Html.RenderAction() call, and step through the execution pipeline. That will expose any advanced functionality—such as intermediate IControllerFactory or IActionFilter code being applied—and will also allow you to identify exactly when and where the code is calling into your GetWorldMapMethod() action.

    Conclusion

    As noted at the top, I'm afraid I can't provide a concrete answer to exactly how your GetWorldMapMethod() is getting called based on the information provided. But, hopefully, this answer will provide you with a solid enough foundation to understand the variables and reverse engineer the code base you're working with.

    If you can follow up with your controller code, I'll be happy to update this answer with more specific guidance as well.