How do I get the MethodInfo of an action, given action, controller and area names?

前端 未结 4 609
被撕碎了的回忆
被撕碎了的回忆 2020-12-06 07:19

What I have is the following extension method:

public MyCustomAttribute[] GetActionAttributes(
    this Controller @this,
    string action,
    string contr         


        
4条回答
  •  伪装坚强ぢ
    2020-12-06 08:02

    I have looked inside MVC 3 source code, and tested with MVC 4, and discovered how to do it. I have tagged the question wrong... it is not for MVC 3, I am using MVC 4. Though, as I could find a solution looking at MVC 3 code, then it may work with MVC 3 too.

    At the end... I hope this is worth 5 hours of exploration, with a lot trials and errors.

    Works with

    • MVC 3 (I think)
    • MVC 4 (tested)

    Drawbacks of my solution

    Unfortunately, this solution is quite complex, and dependent on things that I don't like very much:

    • static object ControllerBuilder.Current (very bad for unit testing)
    • a lot of classes from MVC (high coupling is always bad)
    • not universal (it works with MVC 3 default objects, but may not work with other implementations derived from MVC... e.g. derived MvcHandler, custom IControllerFactory, and so on ...)
    • internals dependency (depends on specific aspects of MVC 3, (MVC 4 behaves like this too) may be MVC 5 is different... e.g. I know that RouteData object is not used to find the controller type, so I simply use stub RouteData objects)
    • mocks of complex objects to pass data (I needed to mock HttpContextWrapper and HttpRequestWrapper in order to set the http method to be POST or GET... these pretty simple values comes from complex objects (oh god! =\ ))

    The code

    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()
            .ToArray();
    
        return attributes;
    }
    

    EDIT

    Forgot the mocked classes

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

    Hope all of this helps someone...

    Happy coding for everybody!

提交回复
热议问题