7

我所拥有的是以下扩展方法:

public MyCustomAttribute[] GetActionAttributes(
    this Controller @this,
    string action,
    string controller,
    string area,
    string method)
{
}

给定区域、控制器、动作名称和方法(GET、POST),ASP.NET MVC 3 如何找到动作方法?

到目前为止,我什么都没有……没有关于如何做到这一点的线索。

我目前正在寻找控制器操作中的堆栈跟踪,以了解 MVC 是如何发现它的。

为什么我需要这些属性

我的属性包含有关给定用户是否可以访问它的信息......但取决于他们是否可以访问它,我不想显示或隐藏一些可能调用该操作的 html 字段、链接和其他内容.

其他用途

我曾想过使用它在动作上放置一个属性,告诉将要渲染的链接的 css 类来调用它......以及其他一些 UI 提示......然后构建一个 HtmlHelper 来呈现该链接,查看这些属性。

不是重复的

是的,有些人会说这可能是这个问题的重复......没有我想要的答案:

如何获取将在给定请求时调用的控制器操作的 MethodInfo?

这就是为什么我指定了我的问题的情况。

4

4 回答 4

11

我查看了 MVC 3 源代码,并使用 MVC 4 进行了测试,并发现了如何做到这一点。我错误地标记了这个问题......它不适用于 MVC 3,我使用的是 MVC 4。虽然,我可以找到一个查看 MVC 3 代码的解决方案,那么它也可能适用于 MVC 3。

最后......我希望这值得 5 个小时的探索,有很多试验和错误。

适用于

  • MVC 3(我认为)
  • MVC 4(已测试)

我的解决方案的缺点

不幸的是,这个解决方案非常复杂,并且依赖于我不太喜欢的东西:

  • 静态对象ControllerBuilder.Current(对单元测试非常不利)
  • 来自 MVC 的很多类(高耦合总是不好的)
  • 不通用(它适用于 MVC 3 默认对象,但可能不适用于从 MVC 派生的其他实现......例如派生的 MvcHandler、自定义 IControllerFactory 等等......)
  • 内部依赖(取决于 MVC 3 的特定方面,(MVC 4 的行为也是如此)可能是 MVC 5 不同...例如,我知道RouteData对象不用于查找控制器类型,所以我只是使用存根 RouteData 对象)
  • 模拟复杂对象以传递数据(我需要模拟HttpContextWrapperHttpRequestWrapper设置http methodPOSTGET......这些非常简单的值来自复杂对象(哦上帝!=\))

编码

public static Attribute[] GetAttributes(
    this Controller @this,
    string action = null,
    string controller = null,
    string method = "GET")
{
    var actionName = action
        ?? @this.RouteData.GetRequiredString("action");

    var controllerName = controller
        ?? @this.RouteData.GetRequiredString("controller");

    var controllerFactory = ControllerBuilder.Current
        .GetControllerFactory();

    var controllerContext = @this.ControllerContext;

    var otherController = (ControllerBase)controllerFactory
        .CreateController(
            new RequestContext(controllerContext.HttpContext, new RouteData()),
            controllerName);

    var controllerDescriptor = new ReflectedControllerDescriptor(
        otherController.GetType());

    var controllerContext2 = new ControllerContext(
        new MockHttpContextWrapper(
            controllerContext.HttpContext.ApplicationInstance.Context,
            method),
        new RouteData(),
        otherController);

    var actionDescriptor = controllerDescriptor
        .FindAction(controllerContext2, actionName);

    var attributes = actionDescriptor.GetCustomAttributes(true)
        .Cast<Attribute>()
        .ToArray();

    return attributes;
}

编辑

忘记了模拟类

class MockHttpContextWrapper : HttpContextWrapper
{
    public MockHttpContextWrapper(HttpContext httpContext, string method)
        : base(httpContext)
    {
        this.request = new MockHttpRequestWrapper(httpContext.Request, method);
    }

    private readonly HttpRequestBase request;
    public override HttpRequestBase Request
    {
        get { return request; }
    }

    class MockHttpRequestWrapper : HttpRequestWrapper
    {
        public MockHttpRequestWrapper(HttpRequest httpRequest, string httpMethod)
            : base(httpRequest)
        {
            this.httpMethod = httpMethod;
        }

        private readonly string httpMethod;
        public override string HttpMethod
        {
            get { return httpMethod; }
        }
    }
}

希望所有这些对某人有帮助...

祝大家编码愉快!

于 2012-10-24T07:44:49.763 回答
3

您可以通过使用AuthorizeAttribute来实现此功能。您可以在OnAuthorization方法中获取 Controller 和 Action 名称。请在下面找到示例代码。

 public sealed class AuthorizationFilterAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// Use for validate user permission and  when it also validate user session is active.
        /// </summary>
        /// <param name="filterContext">Filter Context.</param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string actionName = filterContext.ActionDescriptor.ActionName;
            string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            if (!IsUserHasPermission(controller, actionName))
            {
               // Do your required opeation
            }
        }
    }
于 2012-10-24T06:20:32.717 回答
0

这是一个简短的通知!一定要使用 filterContext.RouteData.DataTokens["area"]; 而不是 filterContext.RouteData.Values["area"];

祝你好运。

于 2013-02-07T17:02:39.867 回答
0

如果您配置了默认路由,例如

routes.MapRoute(
        "Area",
        "",
        new { area = "MyArea", controller = "Home", action = "MyAction" }
    );

您可以在控制器操作中获取路由信息,例如

htp://localhost/Admin

会给你

public ActionResult MyAction(string area, string controller, string action)
{
 //area=Admin
 //controller=Home
 //action=MyAction
 //also you can use RouteValues to get the route information
}

这是 Phil Haack RouteDebugger 2.0的一篇很棒的博客文章和实用程序

于 2012-10-24T05:04:59.313 回答