如何使用 [SwaggerOperationFilter] 的依赖注入?
How to use Dependency Injection with [SwaggerOperationFilter]?
我正在使用 Swashbuckle 为我的网站实施 OpenAPI 文档 api。我决定在我的一项操作中使用 [SwaggerOperationFilter]
属性,以改进其响应主体示例。
根据 the documentation,Swashbuckle 的过滤器管道可以使用依赖注入,如下摘录:
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
这是控制器的简化版本
/// <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" };
}
和我正在尝试实现的IOperationFilter
:
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;
}
}
此代码在尝试访问 OpenAPI 文档和 Swagger UI 时无法执行,但出现以下异常:
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)
我知道如果我在 services.AddSwagerGen(...)
调用中注册 IOperationFilter
,我的操作过滤器 将启用 DI。但是这种方法的问题是注册的 IOperationFilter
将被注册为“全局”过滤器,并将应用于我项目中的每个操作方法,我想避免这种情况,因为这个过滤器应该影响我项目中的单个操作方法。
有没有办法继续使用本地(“非全局”)IOperationFilter
(通过 [SwaggerOperationFilter]
)并让我的过滤器注入依赖项?
Is there any way to keep using a local ("non-global") IOperationFilter
(via [SwaggerOperationFilter]
) and have my filter injected with dependencies?
不是按照他们的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);
}
}
//...
可以看出他们使用了 Activator.CreateInstance
,这需要一个默认构造函数,正如在给定异常的堆栈跟踪中所指出的那样。
我正在使用 Swashbuckle 为我的网站实施 OpenAPI 文档 api。我决定在我的一项操作中使用 [SwaggerOperationFilter]
属性,以改进其响应主体示例。
根据 the documentation,Swashbuckle 的过滤器管道可以使用依赖注入,如下摘录:
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
这是控制器的简化版本
/// <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" };
}
和我正在尝试实现的IOperationFilter
:
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;
}
}
此代码在尝试访问 OpenAPI 文档和 Swagger UI 时无法执行,但出现以下异常:
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)
我知道如果我在 services.AddSwagerGen(...)
调用中注册 IOperationFilter
,我的操作过滤器 将启用 DI。但是这种方法的问题是注册的 IOperationFilter
将被注册为“全局”过滤器,并将应用于我项目中的每个操作方法,我想避免这种情况,因为这个过滤器应该影响我项目中的单个操作方法。
有没有办法继续使用本地(“非全局”)IOperationFilter
(通过 [SwaggerOperationFilter]
)并让我的过滤器注入依赖项?
Is there any way to keep using a local ("non-global")
IOperationFilter
(via[SwaggerOperationFilter]
) and have my filter injected with dependencies?
不是按照他们的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);
}
}
//...
可以看出他们使用了 Activator.CreateInstance
,这需要一个默认构造函数,正如在给定异常的堆栈跟踪中所指出的那样。