在控制器内获取控制器和动作名称?

211

为了我们的web应用程序,我需要根据视图-或者更准确地说是生成视图的控制器和操作(以及用户id,但这不是重点)来保存获取和显示项的顺序。

我想,与其在每个控制器动作中自己提供一个标识符(以便用于某些视图相关的数据库输出排序),不如自动从调用它的控制器和动作方法中创建此标识符,这样更安全、更容易实现。

如何在控制器的操作方法内获取控制器和操作的名称?或者需要反射吗?


1
反射会给你处理操作的方法名称,但是你可能更喜欢安德烈的代码返回的操作名称。 - citykid
我基本上只需要一个明确的标识符来标识每个提供视图的操作,所以两种方式都可以胜任。但你是对的,Andrei的答案肯定更优雅。 - Rob
@citykid除了大小写和类名后缀“Controller”之外,它们是否有其他不同的用法? - John
@John,ActionNameAttribute允许C#方法具有任何操作名称:http://msdn.microsoft.com/en-us/library/system.web.mvc.actionnameattribute%28v=vs.118%29.aspx - citykid
@citykid 哦,好的。既然你可以在操作方法上使用“Route”属性指定路由,那么这种功能有点过时了,我猜是吗?另外,重命名控制器也是可能的吗? - John
显示剩余2条评论
14个回答

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

14
在某些情况下,如果您希望在视图文件中获取控制器的名称,则可以使用此代码:this.ViewContext.RouteData.Values["controller"].ToString();它不会改变原意,只是让内容更加易于理解。 - Amogh Natu
如果你要这样做(提供操作和控制器名称),为什么不直接分配它们呢? - user4593252
1
@MetalPhoenix,你能详细说明一下你所谈论的用例吗?OP不需要分配控制器或操作 - 他们只需要以通用方式了解当前正在处理的控制器和操作即可。 - Andrei
1
在第二次阅读时,我是否可能误解了这里的代码片段?...Values["action"]中的“action”是一个键而不是要替换的操作名称(例如“'Pass123' without the quotes”类型的内容)?也就是说:仍然会是Values["action"]而不是Values["yourAction"]吗? - user4593252
请注意,如果您使用 routes.LowercaseUrls = true;,则您的操作名称和控制器名称将被转换为小写。我想知道是否有解决方法。 - Maksim Vi.
显示剩余5条评论

75

以下是一些用于获取信息(包括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;
    }
}

使用方法:

@Html.Controller();
@Html.Action();
@Html.Id();

1
最佳且完整的解决方案,谢谢Jhon。 - Umar Abbas
5
在ASP.NET Core中不起作用,因为HttpContext.Current不再存在。 - Ben

24

可能会有用。我需要在控制器的构造函数中执行操作,而在MVC生命周期的这个时刻,this尚未初始化,ControllerContext = null。为了避免深入MVC生命周期并找到适当的函数名称进行覆盖,我只需在RequestContext.RouteData中找到该操作。

但是为了这样做,与构造函数中的任何HttpContext相关的使用一样,您必须指定完整的命名空间,因为this.HttpContext也还没有初始化。幸运的是,System.Web.HttpContext.Current是静态的。

// 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
}

注意:这可能不是访问HttpContext中所有属性的最受支持的方式,但对于我的应用程序中的RequestContext和Session,它似乎可以正常工作。


我刚试图在MVC 5中覆盖ControllerFactory方法,以尝试在构造后执行公共方法,因为我需要访问HttpContext和Session,但在构造函数中无法正常工作。即使在构造后,这些值也没有设置在工厂可以处理它们的地方,因此必须在控制器工厂完成其工作后设置它们。 - Logarr

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();
    }
}

6
 @this.ViewContext.RouteData.Values["controller"].ToString();

5

这是我目前的进展:

var actionName = filterContext.ActionDescriptor.ActionName;
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

5
这里是获取名称的最简单、最实用的答案:
var actionName = RouteData.Values["action"];
var controllerName = RouteData.Values["controller"];

或者

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

上面的代码是使用ASP.NET MVC 5进行测试的。


2

对我来说(到目前为止),这似乎很好用,如果您正在使用属性路由,则也适用。

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");
    }
}

2

将以下内容添加到您的基本控制器中的GetDefaults()方法中

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

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

将你的控制器实现到Basecontroller

添加一个局部视图_Breadcrumb.cshtml,并在所有必需的页面中添加@Html.Partial("_Breadcrumb")

_Breadcrumb.cshtml

<span>
    <a href="../@ViewData["controllerName"]">
        @ViewData["controllerName"]
    </a> > @ViewData["actionName"]
</span>

(1) 这仍然是 MVC5 中最常见的方式之一吗? (2) 在 GetDefaults() 中你从哪里获取 filterContext 变量? - chriszo111

1

尝试这段代码

将此覆盖方法添加到控制器中

protected override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
   var actionName = actionExecutingContext.ActionDescriptor.ActionName;
   var controllerName = actionExecutingContext.ActionDescriptor.ControllerDescriptor.ControllerName;
   base.OnActionExecuting(actionExecutingContext);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接