Let’s assume you want to validate something before a request hits your ASP.NET Core controller’s action method. One of the possible solutions is to create an ActionFilterAttribute
and use its OnActionExecutionAsync
(in case you like to do it asynchronously) to do the checking. Now if you are depending on something like configuration or even another instance of a class it is going to be tricky.
ActionFilterAttributes
. For that you can have a look at here.The way you use your filter attribute is to put it above your action like this:
public class ValidateSomethingAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
await DoSomething(context);
await base.OnActionExecutionAsync(context, next);
}
}
public class MyController: Controller
{
[ValidateSomethingAttribute]
public ActionResult Get()
{
}
}
Obviously there are two ways you can pass an input to an action filter. You can pass an input or you can use dependency injector (and I am not talking about scalar values here). The benefit of having dependency injector inject that dependency is that you won’t need to define a public property in your filter and pass it from controller.
Instead you can define a private property and set it from the filter’s constructor:
public class ValidateSomethingAttribute : ActionFilterAttribute
{
private readonly IConfiguration _configuration;
public ValidateSomethingAttribute(IConfiguration configuration)
{
_configuration = configuration;
}
public override async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
await DoSomething(context);
await base.OnActionExecutionAsync(context, next);
}
}
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they are applied. This is a limitation of how attributes work.
Now the question is how to have DI to inject that dependency into our filter for us. The answer is by using another filter called ServiceFilter
.
From Microsoft Docs:
A ServiceFilter
retrieves an instance of the filter from DI. You add the filter to the container in ConfigureServices
, and reference it in a ServiceFilter
attribute.
All we need to do is to let DI know it should resolve our filter attribute and inject it using a service filter:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ValidateSomethingAttribute>();
}
and in the controller:
[ServiceFilter(typeof(ValidateSomethingAttribute))]
public IActionResult Index()
{
return View();
}
And done, you now can depend on ASP.NET Core DI to handle everything for you and not worry about initialising objects with their dependencies.
To me this way is very neat and better than trying to pass the dependencies as parameters, especially if the number of dependencies are high.