How can I get a MethodInfo action, given the action, controller name and scope?

I have the following extension method:

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

How does ASP.NET MVC 3 find the action method given the scope, controller, action names and method (GET, POST)?

At this point, I have nothing ... no clues on how to do this.

I'm currently looking for a stack trace inside a controller action to find out how MVC learned it.

I need these attributes

My attributes contain information about whether a given user can or cannot access him ... but depending on whether they can or not, I don’t want to show or hide some html-fields, links and other things, which could trigger this action.

Other uses

I thought about using this to put an attribute on an action that tells the css class of the link that will be displayed to call it ... and some other interface hints ... and then create an HtmlHelper that will render this link by looking at these attributes .

Not duplicated

Yes, some will say that this is possibly a duplicate of this question ... I have no answer:

How can I get the MethodInfo action of the controller that is being called with the request?

That is why I indicated the circumstances of my question.

+5
source share
4 answers

I looked into the source code of MVC 3 and tested it with MVC 4 and discovered how to do it. I incorrectly noted the question ... this is not for MVC 3, I am using MVC 4. Although, since I could find a solution looking at MVC 3 code, it can also work with MVC 3.

In the end ... I hope it costs 5 hours of research, with lots of trial and error.

Works with

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

Disadvantages of my solution

Unfortunately, this solution is quite complicated and depends on things that I don't really like:

  • static object ControllerBuilder.Current (very bad for unit testing)
  • many classes from MVC (high connection is always bad)
  • not universal (it works with MVC 3 objects by default, but may not work with other implementations derived from MVC ... for example, a derivative of MvcHandler, custom IControllerFactory, etc.)
  • internal dependency (depends on specific aspects of MVC 3 (MVC 4 may also behave) MVC 5 is different ... for example, I know that the RouteData object RouteData not used to find the type of controller, so I just use RouteData stub objects)
  • mocks of complex objects for data transfer (I needed to make fun of HttpContextWrapper and HttpRequestWrapper to set the http method as POST or GET ... these pretty simple values ​​come 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<Attribute>() .ToArray(); return attributes; } 

EDIT

Forgot mocking 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 this helps someone ...

Happy coding for everyone!

+9
source

You can achieve this functionality using AuthorizeAttribute . You can get the name of the controller and action in the OnAuthorization method . PLease find the sample code below.

  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 } } } 
+3
source

if you have a default route configured as

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

you can get route information inside the controller action, for example

ht tp: // localhost / admin

will provide you

 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 } 

here is a great blog post and the usefulness of Phil Haack RouteDebugger 2.0

0
source

This is a short notice! Be sure to use filterContext.RouteData.DataTokens ["area"]; instead of filterContext.RouteData.Values ​​["area"];

Good luck.

0
source

Source: https://habr.com/ru/post/920799/


All Articles