Santos
Santos

Reputation: 37

How to pass parameter of different type to a generic ServiceFilter or ActionFilter

I am not very experienced on C# and ASP, and I am not sure I am taking the right approach to this problem.

This is a brief representation of it:

I have several endpoints related to an user but with different parameters (eg. string or UserDTO) Here are a couple of them:

[AllowAnonymous]
[HttpPut("{username}")]
// I want to pass [FromBody]UserUpdateDTO to the filter
[ServiceFilter(typeof(UserChangeManagerFilter<UserUpdateDTO>))]
public ActionResult<InternalStatus> UpdateUser([FromBody]UserUpdateDTO userDto)
{
...
}

[AllowAnonymous]
[HttpDelete("{username}")]
// I want to pass {username} to the filter 
[ServiceFilter(typeof(UserChangeManagerFilter<string>))]
public ActionResult<InternalStatus> DeleteUser(string username)
{
...
}

Now, I need to execute some code after each action is executed. For this, I have created a generic filter My problem is that the filter needs to know the user details, so the PUT endpoint has to pass the UserDTO element to the filter, and the DELETE endpoint has to pass the username. I don't know how to do that exactly, but researching, I have seen that using a generic sercive filter was an option. So I created this one:

namespace ActionFilters.ActionFilters
{
    public class UserChangeManagerFilter<T> : IActionFilter
    {
        private UserInfo _user;

        public UserChangeManagerFilter(UserUpdateDTO userContext)
        {
            _user.Username = userContext.Username;
            _user.Firstname = userContext.FirstName; 
            ...
        }
        public UserChangeManagerFilter(string username)
        {
            _user.Username = username;
            _user.Firstname = ""; 
            ...
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {

        }
        public void OnActionExecuted(ActionExecutedContext context)
        {
              // Do whatever I need to do here
        }
}

I am adding the service in Startup.cs as below:

services.AddScoped<UserChangeManagerFilter<UserUpdateDTO>>();
services.AddScoped<UserChangeManagerFilter<string>>();

When I execute the PUT endpoint, I get

System.InvalidOperationException: No constructor for type 'ActionFilters.ActionFilters.UserChangeManagerFilter`1[xxx.Services.UserUpdateDTO]' can be instantiated using services from the service container and default values.

When I execute the DELETE endpoint I get

System.InvalidOperationException: No constructor for type 'ActionFilters.ActionFilters.UserChangeManagerFilter`1[System.String]' can be instantiated using services from the service container and default values.

Upvotes: 0

Views: 807

Answers (1)

JTMon
JTMon

Reputation: 3199

I know this is not a direct answer to your question but how about using a non generic filter and then using the OnActionExecuting to inspect the model being passed and extracting the data as needed depending on the type of object received:

   public void OnActionExecuting(ActionExecutingContext context)
   {
       var userDto = actionContext.ActionArguments.Values.OfType<UserUpdateDTO>().Single();
       if(userDto !=null)
       {
           _user.Username = userContext.Username;
           _user.Firstname = userContext.FirstName;
           ...
       }else
       {
           //look for the string value
       }
   }

Upvotes: 1

Related Questions