Get controller and action name from within controller?
Asked Answered
H

14

212

For our web application I need to save the order of the fetched and displayed items depending on the view - or to be precise - the controller and action that generated the view (and the user id of course, but that's not the point here).

Instead of just giving an identifier myself in each controller action (in order to use it for some view-dependant sorting of DB outputs), I thought that it would be safer and easier to create this identifier automatically from the controller and action method it gets called from.

How can I get the name of the controller and action from within the action method in a controller? Or do I need reflection for that?

Hautesavoie answered 15/8, 2013 at 8:3 Comment(7)
Reflection would give you the method name that handles the action, but presumably you prefer the action name as returned by Andrei's code.Cirrate
I basically just need an unambiguous identifier for every action that delivers a view, so both ways would do the job. But you're right, Andrei's answer is definitely more elegant.Hautesavoie
@Cirrate Are there cases where these differ in manners other than case and the "Controller" suffix for class names?Langlauf
@John, ActionNameAttribute allows a c# method to have any action name: msdn.microsoft.com/en-us/library/…Cirrate
@Cirrate Oh, ok. That's kind of an obsolete feature given that you can specify the routes with a Route attribute on the action method I gather? Also, is it also possible to rename controllers?Langlauf
@Cirrate (Addition to last comment after some more research: It appears that the "Route" attribute is newer and probably obsoletes the "Action" attribute somewhat. Also, the renaming the controllers would be more unusual but can by creating a custom controller factory. It appears that you can't then get the name from the class anymore though.)Langlauf
sure sure, the Actionname attribute is however still there, working and not marked as obsolete. in the common case the marked answer is the best way to do it anyway.Cirrate
B
399
string actionName = this.ControllerContext.RouteData.Values["action"].ToString();
string controllerName = this.ControllerContext.RouteData.Values["controller"].ToString();
Brower answered 15/8, 2013 at 8:6 Comment(7)
In some case where you might want to have the name of the controller in the View file, then you can just use this.ViewContext.RouteData.Values["controller"].ToString();Squamous
If you're going to do this (provide the action and controller name), why not just assign them directly???Heptavalent
@MetalPhoenix, can you clarify a bit what use case you are talking about? OP does not need to assign controller or action - they just need to understand, in generic way, what are the controller and action currently being processed.Brower
On a second read, is it possible that I misunderstood the code snippit here? ...Values["action"] where "action" is a key and not the name of the action to be substituted (like "'Pass123' without the quotes" type of thing)? That is to say: would still be Values["action"] instead of Values["yourAction"]?Heptavalent
@MetalPhoenix, exactly, "action" literal is a key, and Values["action"] will output "CurrentActionName"Brower
Note that if you use routes.LowercaseUrls = true; your action names and contollers will be lowecased. I wonder if there's a workaround.Joplin
Those names will reflect the casing of request url. What you do with them should not be case-sensitive. (I fell in that trap.)Tedric
T
75

Here are some extension methods for getting that information (it also includes the ID):

public static class HtmlRequestHelper
{
    public static string Id(this HtmlHelper htmlHelper)
    {
        var routeValues = HttpContext.Current.Request.RequestContext.RouteData.Values;

        if (routeValues.ContainsKey("id"))
            return (string)routeValues["id"];
        else if (HttpContext.Current.Request.QueryString.AllKeys.Contains("id"))
            return HttpContext.Current.Request.QueryString["id"];

        return string.Empty;
    }

    public static string Controller(this HtmlHelper htmlHelper)
    {
        var routeValues = HttpContext.Current.Request.RequestContext.RouteData.Values;

        if (routeValues.ContainsKey("controller"))
            return (string)routeValues["controller"];

        return string.Empty;
    }

    public static string Action(this HtmlHelper htmlHelper)
    {
        var routeValues = HttpContext.Current.Request.RequestContext.RouteData.Values;

        if (routeValues.ContainsKey("action"))
            return (string)routeValues["action"];

        return string.Empty;
    }
}

Usage:

@Html.Controller();
@Html.Action();
@Html.Id();
Tittle answered 3/10, 2014 at 18:20 Comment(2)
Best & Complete Solution, Thanks JhonRamsay
Doesn't work in ASP.NET Core because HttpContext.Current doesn't exist anymoreDefrock
U
24

Might be useful. I needed the action in the constructor of the controller, and it appears at this point of the MVC lifecycle, this hasn't initialized, and ControllerContext = null. Instead of delving into the MVC lifecycle and finding the appropriate function name to override, I just found the action in the RequestContext.RouteData.

But in order to do so, as with any HttpContext related uses in the constructor, you have to specify the full namespace, because this.HttpContext also hasn't been initialized. Luckily, it appears System.Web.HttpContext.Current is static.

// controller constructor
public MyController() {
    // grab action from RequestContext
    string action = System.Web.HttpContext.Current.Request.RequestContext.RouteData.GetRequiredString("action");

    // grab session (another example of using System.Web.HttpContext static reference)
    string sessionTest = System.Web.HttpContext.Current.Session["test"] as string
}

NOTE: likely not the most supported way to access all properties in HttpContext, but for RequestContext and Session it appears to work fine in my application.

Uncle answered 24/10, 2013 at 15:58 Comment(1)
I just tried overriding the ControllerFactory methods in MVC 5 to try and execute a common method after construction, because I needed to get at the HttpContext and Session and it wasn't working in the constructor. Even after it has been constructed, the values are not set where the factory can do anything with them, so they must be set after the controller factory is done doing its thing.Occlusive
G
14
var routeValues = HttpContext.Current.Request.RequestContext.RouteData.Values;
if (routeValues != null) 
{
    if (routeValues.ContainsKey("action"))
    {
        var actionName = routeValues["action"].ToString();
    }
    if (routeValues.ContainsKey("controller"))
    {
        var controllerName = routeValues["controller"].ToString();
    }
}
Geostrophic answered 5/2, 2014 at 18:52 Comment(0)
S
6
 @this.ViewContext.RouteData.Values["controller"].ToString();
Snorter answered 5/9, 2017 at 6:10 Comment(0)
C
5

This is what I have so far:

var actionName = filterContext.ActionDescriptor.ActionName;
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
Checkoff answered 9/11, 2015 at 22:30 Comment(0)
I
5

Here is the simplest and most practical answer to getting a name:

var actionName = RouteData.Values["action"];
var controllerName = RouteData.Values["controller"];

Or

string actionName = RouteData.Values["action"].ToString();
string controllerName = RouteData.Values["controller"].ToString();

Code above tests with asp.net mvc 5.

Inguinal answered 26/6, 2017 at 14:15 Comment(0)
J
2

This seems to work nicely for me (so far), also works if you are using attribute routing.

public class BaseController : Controller
{
    protected string CurrentAction { get; private set; }
    protected string CurrentController { get; private set; }

    protected override void Initialize(RequestContext requestContext)
    {
        this.PopulateControllerActionInfo(requestContext);
    }

    private void PopulateControllerActionInfo(RequestContext requestContext)
    {
        RouteData routedata = requestContext.RouteData;

        object routes;

        if (routedata.Values.TryGetValue("MS_DirectRouteMatches", out routes))
        {
            routedata = (routes as List<RouteData>)?.FirstOrDefault();
        }

        if (routedata == null)
            return;

        Func<string, string> getValue = (s) =>
        {
            object o;
            return routedata.Values.TryGetValue(s, out o) ? o.ToString() : String.Empty;
        };

        this.CurrentAction = getValue("action");
        this.CurrentController = getValue("controller");
    }
}
Joelynn answered 16/1, 2016 at 2:50 Comment(0)
L
2

Add this to your base controller inside GetDefaults() method

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
         GetDefaults();
         base.OnActionExecuting(filterContext);
    }

    private void GetDefaults()
    {
    var actionName = filterContext.ActionDescriptor.ActionName;
    var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
    }

Implement your controllers to Basecontroller

Add a partial view _Breadcrumb.cshtml and add it in all required pages with @Html.Partial("_Breadcrumb")

_Breadcrumb.cshtml

<span>
    <a href="../@ViewData["controllerName"]">
        @ViewData["controllerName"]
    </a> > @ViewData["actionName"]
</span>
Lemonade answered 21/2, 2017 at 19:22 Comment(1)
(1): Is this still one of the most common ways within MVC5? (2) Where do you get your filterContext variable from within GetDefaults()?Basidiomycete
A
1

You can get controller name or action name from action like any variable. They are just special (controller and action) and already defined so you do not need to do anything special to get them except telling you need them.

public string Index(string controller,string action)
   {
     var names=string.Format("Controller : {0}, Action: {1}",controller,action);
     return names;
   }

Or you can include controller , action in your models to get two of them and your custom data.

public class DtoModel
    {
        public string Action { get; set; }
        public string Controller { get; set; }
        public string Name { get; set; }
    }

public string Index(DtoModel baseModel)
    {
        var names=string.Format("Controller : {0}, Action: {1}",baseModel.Controller,baseModel.Action);
        return names;
    }
Aldose answered 31/7, 2015 at 15:2 Comment(0)
S
1

To remove need for ToString() call use

string actionName = ControllerContext.RouteData.GetRequiredString("action");
string controllerName = ControllerContext.RouteData.GetRequiredString("controller");
Searles answered 29/1, 2017 at 19:32 Comment(0)
F
1

Try this code

add this override method to controller

protected override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
   var actionName = actionExecutingContext.ActionDescriptor.ActionName;
   var controllerName = actionExecutingContext.ActionDescriptor.ControllerDescriptor.ControllerName;
   base.OnActionExecuting(actionExecutingContext);
}
Fishhook answered 22/1, 2021 at 6:37 Comment(0)
E
1

I know this is an old question, but maybe this can help to someone. For add to the John B answer, this is a solution for .NET 6.0 or higher.

namespace Microsoft.AspNetCore.Mvc.Rendering
    {
    public static class HtmlRequestHelper
    {
        public static string Id(this IHtmlHelper htmlHelper)
        {
            var routeValues = htmlHelper.ViewContext.RouteData.Values;

            if (routeValues.ContainsKey("id"))
                return (string)routeValues["id"];
            else if (htmlHelper.ViewContext.RouteData.Values["id"] != null)
                return htmlHelper.ViewContext.RouteData.Values["id"].ToString();

            return string.Empty;
        }

        public static string Controller(this IHtmlHelper htmlHelper)
        {
            var routeValues = htmlHelper.ViewContext.RouteData.Values;

            if (routeValues.ContainsKey("controller"))
                return (string)routeValues["controller"];

            return string.Empty;
        }

        public static string Action(this IHtmlHelper htmlHelper)
        {
            var routeValues = htmlHelper.ViewContext.RouteData.Values;

            if (routeValues.ContainsKey("action"))
                return (string)routeValues["action"];

            return string.Empty;
        }
    }
}
Exigent answered 26/11, 2023 at 23:8 Comment(0)
F
-8

Why not having something simpler?

Just call Request.Path, it will return a String Separated by the "/"

and then you can use .Split('/')[1] to get the Controller Name.

enter image description here

Feeley answered 5/4, 2016 at 3:12 Comment(3)
-1: with your code, sub-level applications are simply ignored (eg: http://www.example.com/sites/site1/controllerA/actionB/). MVC provides a bunch of API's for routing, so why you need to parse (again) URLs?.Homophonous
Why to reinvent the wheel and furthermore, with a poor reinvension? this does not work for all cases.Laminous
asides from subfolders, the real problem is that you can customize your routes so they won't always be controller/actionJoanne

© 2022 - 2025 — McMap. All rights reserved.