Asp.NET MVC ModelBinder, getting Action Method
I got a custom ModelBinder and i would like to get the action. Because i want to get the Attributes of the action using reflection, the action name is not enough.
my action method:
[MyAttribute]
public ActionResult Index([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
}
and here a typically ModelBinder
public class MyModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// here i would like to get the action method and his "MyAttribute"
}
}
any suggestions, other solutio开发者_如何学JAVAns ?
many thanks in advance
No, you cannot with 100% certainty get the current action from a model binder. The model binder is not coupled to the action, but to binding to a model. For example, you can call
TryUpdateMode(model)
In an filter before an action has been chosen. Also note that an action method might not even be a CLR method (see http://haacked.com/archive/2009/02/17/aspnetmvc-ironruby-with-filters.aspx) that can be reflected on.
I think the real question is, what exactly are you trying to accomplish and is this the right way? If you want information from the action to be passed to the model binder (heeding the advice that your model binder should degrade gracefully if the information isn't there), you should use an action filter to put the information in HttpContext.Items (or somewhere like that) and then have your binder retrieve it.
An action filter's OnActionExecuting method receives an ActionExecutingContext which has an ActionDescriptor. You can call GetCustomAttributes on that.
You could try this:
var actionName = controllerContext.RouteData.GetRequiredString("action");
var myAttribute = (MyAttribute) Attribute.GetCustomAttribute(controllerContext.Controller.GetMethod(actionName), typeof(MyAttribute));
You could override ControllerActionInvoker.FindAction()
to get the action's attribute and store it in HttpContext.Current.Items
as mentioned here, or extendedControllerContext.RequestContext
, as follows:
public class MyControllerActionInvoker : ControllerActionInvoker
{
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
if (action != null)
{
var requestContext = ExtendedRequestContext.Bind(controllerContext);
var attr = action.GetCustomAttributes(typeof(MyAttribute), false).FirstOrDefault();
if (attr != null)
requestContext.CustomAttribute = (MyAttribute)attr;
}
return action;
}
}
public class ExtendedRequestContext : RequestContext
{
public MyAttribute CustomAttribute { get; set; }
public static ExtendedRequestContext Bind(ControllerContext controllerContext)
{
var requestContext = new ExtendedRequestContext
{
HttpContext = controllerContext.RequestContext.HttpContext,
RouteData = controllerContext.RequestContext.RouteData
};
controllerContext.RequestContext = requestContext;
return requestContext;
}
}
The default action invoker is replaced either in your controller's constructor or in a custom controllers factory:
public MyController() : base()
{
ActionInvoker = new MyControllerActionInvoker();
}
By the way, Controller.TempData
already contains an item of ReflectedParameterDescriptor
type, which gives you access to ActionDescriptor
, so the above code may be redundant. However, beware this is implementation specific, so may change over time.
Finally, get the attribute from that storage in your binder class:
var requestContext = (ExtendedRequestContext)controllerContext.RequestContext;
if (requestContext.CustomAttribute != null)
{
// apply your logic here
}
精彩评论