Search code examples
c#asp.net-coreasp.net-core-mvc

How to add ModelStateInvalidFilter as a global filter?


I'm trying to add ModelStateInvalidFilter as a global filter so that I don't have to add ApiController attribute on every controllers.

This is what I'm doing in my Startup

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ApiBehaviorOptions>();
    services.AddScoped<ModelStateInvalidFilter>();

    services.AddMvc(config => config.Filters.AddService<ModelStateInvalidFilter>())
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);                              
}

However, I'm getting this error

InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter'.

I know the generic ILogger<T> is automatically injected by the framework but how do I inject the non-generic ILogger which is a dependency of the ModelStateInvalidFilter class?


Solution

  • When you use [ApiController], ModelStateInvalidFilter isn't resolved from the DI container, it's created with new:

    _modelStateInvalidFilter = new ModelStateInvalidFilter(
        apiBehaviorOptions.Value,
        loggerFactory.CreateLogger<ModelStateInvalidFilter>());
    

    In order to be able to resolve this from the DI container, we'll need to be a bit more explicit about how we create an instance of ILogger, which can be done when adding ModelStateInvalidFilter to the DI container, like this:

    services.AddScoped<ModelStateInvalidFilter>(sp =>
    {
        var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
    
        return new ModelStateInvalidFilter(
            sp.GetRequiredService<ApiBehaviorOptions>(),
            loggerFactory.CreateLogger<ModelStateInvalidFilter>());
    });
    

    In this example, sp is the instance of IServiceProvider that we can use to request services from the DI container. ILoggerFactory has already been registered for us, so we can request it and use it in the same way shown in the first code snippet above.