Rafael Companhoni
Rafael Companhoni

Reputation: 1790

web api 2 - Passing data from action filter to action as an argument

In order to avoid getting the user data on every action I've create an custom action filter that gets the user by its ID and then passes to the action.

public class UserDataAttribute : ActionFilterAttribute
{    
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            ...
            // getting the user and storing it in the request properties
            object user = userBLL.GetUserById(userId);
            actionContext.Request.Properties.Add("User", user);
        }
}

And the I can get the user object in the action method like this:

[Authorize]
[UserData]
[HttpGet]
[Route("dosomething")]
public IHttpActionResult DoSomething()
{
      // retrieve the user
      object user;
      Request.Properties.TryGetValue("User", out user);
      User u = (User)user;

      return Ok();
}

However, in MVC it's possible to use ActionParameters in the filter to store something that will be used by the action method, like so:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
     ...

     // Create object parameter.
     filterContext.ActionParameters["User"] = userBLL.GetUserById(userId);
 }

And then use the User object as if it were part of the original request:

[AddActionParameter]
public ActionResult Index(User user)
{
    // Here I can access the user setted on the filter
    ...

    return View();
}

So, my question is: There is a way in Web API 2 to pass the User object from the action filter to the action as an argument, just like in MVC?

Upvotes: 1

Views: 2786

Answers (1)

With ASP.NET Web API, you can create a parameter binding to receive an object, User in your case. You don't have to create a filter for this. So, you will create a binding like this.

public class UserParameterBinding : HttpParameterBinding
{
    public UserParameterBinding(HttpParameterDescriptor descriptor) : 
                                                        base(descriptor) { }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, 
                                               HttpActionContext context,
                                                  CancellationToken cancellationToken)
    {
        SetValue(context, new User() { // set properties here });

        return Task.FromResult<object>(null);
    }
}

Then, to use the binding, you will configure it, like this.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // snip

        config.ParameterBindingRules.Insert(0, d => 
            d.ParameterType == typeof(User) ? new UserParameterBinding(d) : null);

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

With that, wherever you have User as action method parameter, it will automatically bind the instance you are creating inside UserParameterBinding to that parameter.

Upvotes: 4

Related Questions