210

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?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Rob
  • 11,492
  • 14
  • 59
  • 94
  • 1
    Reflection would give you the method name that handles the action, but presumably you prefer the action name as returned by Andrei's code. – citykid Aug 15 '13 at 09:03
  • 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. – Rob Aug 15 '13 at 09:22
  • @citykid Are there cases where these differ in manners other than case and the "Controller" suffix for class names? – John Nov 04 '14 at 14:15
  • @John, ActionNameAttribute allows a c# method to have any action name: http://msdn.microsoft.com/en-us/library/system.web.mvc.actionnameattribute%28v=vs.118%29.aspx – citykid Nov 05 '14 at 05:38
  • @citykid 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? – John Nov 05 '14 at 08:30
  • @citykid (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.) – John Nov 05 '14 at 08:38
  • 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. – citykid Nov 05 '14 at 08:55

13 Answers13

396
string actionName = this.ControllerContext.RouteData.Values["action"].ToString();
string controllerName = this.ControllerContext.RouteData.Values["controller"].ToString();
L_7337
  • 2,650
  • 28
  • 42
Andrei
  • 55,890
  • 9
  • 87
  • 108
  • 14
    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(); – Amogh Natu Dec 24 '14 at 04:56
  • If you're going to do this (provide the action and controller name), why not just assign them directly??? – user4593252 Apr 03 '15 at 19:16
  • 1
    @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. – Andrei Apr 07 '15 at 09:48
  • 1
    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"]? – user4593252 Apr 08 '15 at 12:42
  • @MetalPhoenix, exactly, "action" literal is a key, and Values["action"] will output "CurrentActionName" – Andrei Apr 08 '15 at 12:47
  • Note that if you use `routes.LowercaseUrls = true;` your action names and contollers will be lowecased. I wonder if there's a workaround. – Maksim Vi. Jul 04 '15 at 01:42
  • Those names will reflect the casing of request url. What you do with them should not be case-sensitive. (I fell in that trap.) – Frédéric Dec 21 '15 at 15:26
74

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();
Kevin Montrose
  • 22,191
  • 9
  • 88
  • 137
John B
  • 20,062
  • 35
  • 120
  • 170
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.

sonjz
  • 4,870
  • 3
  • 42
  • 60
  • 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. – Logarr Jul 21 '21 at 17:21
13
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();
    }
}
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53
Chris Ballance
  • 33,810
  • 26
  • 104
  • 151
6
 @this.ViewContext.RouteData.Values["controller"].ToString();
Hossein Hajizadeh
  • 1,357
  • 19
  • 10
5

This is what I have so far:

var actionName = filterContext.ActionDescriptor.ActionName;
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
Andrew
  • 3,874
  • 5
  • 39
  • 67
user3563149
  • 51
  • 1
  • 1
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.

tkerwood
  • 1,875
  • 16
  • 26
Matheus Miranda
  • 1,755
  • 2
  • 21
  • 36
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");
    }
}
joepour
  • 6,831
  • 10
  • 32
  • 29
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>
Kurkula
  • 6,386
  • 27
  • 127
  • 202
  • (1): Is this still one of the most common ways within MVC5? (2) Where do you get your `filterContext` variable from within `GetDefaults()`? – chriszo111 Nov 21 '17 at 15:57
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;
    }
Mustafa ASAN
  • 3,747
  • 2
  • 23
  • 34
1

To remove need for ToString() call use

string actionName = ControllerContext.RouteData.GetRequiredString("action");
string controllerName = ControllerContext.RouteData.GetRequiredString("controller");
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
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);
}
-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

adie wong
  • 51
  • 6
  • 1
    -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?. – T-moty Apr 15 '16 at 13:05
  • Why to reinvent the wheel and furthermore, with a poor reinvension? this does not work for all cases. – jstuardo Jul 10 '17 at 13:33
  • asides from subfolders, the real problem is that you can customize your routes so they won't always be `controller/action` – drzaus Jul 12 '18 at 15:55