How to get current model in action filter

2020-02-11 21:01发布

问题:

I have a generic action filter, and i want to get current model in the OnActionExecuting method. My current implementation is like below:

public class CommandFilter<T> : IActionFilter where T : class, new()
{
    public void OnActionExecuting(ActionExecutingContext actionContext)
    {
        var model= (T)actionContext.ActionArguments["model"];
    }
}

It works well if my all model names are same. But i want to use differnet model names.

How to solve this problem?

Edit

public class HomeController : Controller
{
    [ServiceFilter(typeof(CommandActionFilter<CreateInput>))]
    public IActionResult Create([FromBody]CreateInput model)
    {
        return new OkResult();
    }
}

回答1:

ActionExecutingContext.ActionArguments is just a dictionary,

    /// <summary>
    /// Gets the arguments to pass when invoking the action. Keys are parameter names.
    /// </summary>
    public virtual IDictionary<string, object> ActionArguments { get; }

And you need to loop through it if you need to avoid hardcoded parameter name ("model"). From the same SO answer for asp.net:

When we create a generic action filter that needs to work on a class of similar objects for some specific requirements, we could have our models implement an interface => know which argument is the model we need to work on and we can call the methods though the interface.

In your case you may write something like this:

public void OnActionExecuting(ActionExecutingContext actionContext)
{
    foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is T))
    {
         T model = argument as T;
         // your logic
    }
}


回答2:

You can use ActionExecutingContext.Controller property

    /// <summary>
    /// Gets the controller instance containing the action.
    /// </summary>
    public virtual object Controller { get; }

and converting result to base MVC Controller get access to model:

((Controller)actionExecutingContext.Controller).ViewData.Model


回答3:

In case your controller action has multiple arguments and in your filter you want to select the one that is bound via [FromBody], then you can use reflection to do the following:

public void OnActionExecuting(ActionExecutingContext context)
{
    foreach (ControllerParameterDescriptor param in context.ActionDescriptor.Parameters) {
        if (param.ParameterInfo.CustomAttributes.Any(
            attr => attr.AttributeType == typeof(FromBodyAttribute))
        ) {             
            var entity = context.ActionArguments[param.Name];

            // do something with your entity...