Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Dependency Injection with [SwaggerOperationFilter]?

I'm using Swashbuckle to implement an OpenAPI documentation for my web api. I have decided to use a [SwaggerOperationFilter] attribute in one of my operations in order to improve its Response Body example.

According to the documentation, Swashbuckle's filter pipelines can use Dependency Injection, as per the following exerpt:

NOTE: Filter pipelines are DI-aware. That is, you can create filters with constructor parameters and if the parameter types are registered with the DI framework, they'll be automatically injected when the filters are instantiated

Here's a simplified version of a controller

/// <summary>Some sample controller.</summary>
[ApiController]
[Route("/my-controller")]
public class MyController : ControllerBase {
    /// <summary>Simple action returning some string array.</summary>
    /// <response code="200">All went well.</response>
    [HttpGet]
    [SwaggerOperationFilter(typeof(MyOperationFilter))]
    public IEnumerable<string> GetSomeStrings() => new[] { "abc", "def" };
}

and the IOperationFilter I'm trying to implement:

public class MyOperationFilter : IOperationFilter {
    public MyOperationFilter(ILogger<MyOperationFilter> logger) {   // <--- Dependency Injection will fail to call this constructor
        logger.LogInformation("DI won't work!");
    }


    public void Apply(OpenApiOperation operation, OperationFilterContext context) {
        var responseExample = new OpenApiArray();
        responseExample.AddRange(new [] {
            new OpenApiString("text-1"),
            new OpenApiString("text-2"),
            new OpenApiString("text-3"),
        });

        var response = operation.Responses["200"];
        response.Content["application/json"].Example = responseExample;
    }
}

This code fails to execute while trying to access the OpenAPI Document and the Swagger UI with the following exception:

MyProject.MyControllers.UnhandledExceptionsController[0]
      Unhandled exception of type SwaggerGeneratorException on path "/api-docs/my-api-v1/openapi.json"
      Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate Operation for action - MyProject.MyControllers.MyController.GetSomeStrings (my-project). See inner exception  
       ---> System.MissingMethodException: No parameterless constructor defined for type 'MyProject.MyControllers.MyOperationFilter'.
         at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
         at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
         at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
         at Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.ApplySwaggerOperationFilterAttributes(OpenApiOperation operation, OperationFilterContext context, IEnumerable`1 controllerAndActionAttributes)
         at Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.Apply(OpenApiOperation operation, OperationFilterContext context)
         at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
         --- End of inner exception stack trace ---
         at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
         at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
         at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
         at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

I understand that if I register the IOperationFilter in the services.AddSwagerGen(...) call, my operation filter will be DI-enabled. But the problem with that approach is that the registered IOperationFilter will be registered as a "global" filter, and will be applied to every action method in my project, and I wanted to avoid that, as this filter was supposed to affect a single action method in my project.

Is there any way to keep using a local ("non-global") IOperationFilter (via [SwaggerOperationFilter]) and have my filter injected with dependencies?

like image 810
vinicius.ras Avatar asked Nov 01 '25 21:11

vinicius.ras


1 Answers

Is there any way to keep using a local ("non-global") IOperationFilter (via [SwaggerOperationFilter]) and have my filter injected with dependencies?

Not according to their source code

AnnotationsOperationFilter

//...

public static void ApplySwaggerOperationFilterAttributes(
    OpenApiOperation operation,
    OperationFilterContext context,
    IEnumerable<object> controllerAndActionAttributes)
{
    var swaggerOperationFilterAttributes = controllerAndActionAttributes
        .OfType<SwaggerOperationFilterAttribute>();

    foreach (var swaggerOperationFilterAttribute in swaggerOperationFilterAttributes)
    {
        var filter = (IOperationFilter)Activator.CreateInstance(swaggerOperationFilterAttribute.FilterType);
        filter.Apply(operation, context);
    }
}

//...

Where it can seen that they use Activator.CreateInstance, which required a default constructor as already indicated in the stack trace of the exception given.

like image 189
Nkosi Avatar answered Nov 03 '25 10:11

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!