NSWAG-常见操作响应操作处理器

发布于 2025-02-10 23:52:20 字数 8201 浏览 2 评论 0原文

我正在使用此处使用干净的体系结构解决方案设置新的API: https://github.com/jasontaylordev/jasontoylordev/cleanarchitecture

如果我们专注于API的返回类型。

1-200-为此生成此API规范(基于控制器中的返回类型),

例如get/dodoItems

"/api/TodoItems": {
  "get": {
    "tags": [
      "TodoItems"
    ],
    "operationId": "TodoItems_GetTodoItemsWithPagination",
    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaginatedListOfTodoItemBriefDto"
            }
          }
        }
      }
    },
    ]
  },

,我们在apiexceptionfiltertribute < /code>在文件夹webui/filters/

_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
            {
                { typeof(ValidationException), HandleValidationException },
                { typeof(NotFoundException), HandleNotFoundException },
                { typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
                { typeof(ForbiddenAccessException), HandleForbiddenAccessException },
            };

typeof(valivationException)用于http status code 400

type> type> typef(notfoundException)代码>用于HTTP状态代码404

typeof(unauterizedAccessexception) for HTTP状态代码401

typef(forbiddenAccessexception)现在是HTTP状态代码403

现在,要使这些填充在Swagger.json文件中,我们将需要使用以下内容来装饰每个控制器操作。

[ProducesResponseType(typeof(ValidationException), 400)]
[ProducesResponseType(typeof(UnauthorizedAccessException), 401)]
[ProducesResponseType(typeof(NotFoundException), 404)]
[ProducesResponseType(typeof(ForbiddenAccessException), 403)]

为了避免这种情况,我想到了使用ioperationProcessornswag中使用。但是我很难为返回类型生成架构。

public class CustomResponseTypesOperationProcessor : IOperationProcessor
{
    public bool Process(OperationProcessorContext context)
    {
        var schema = context.SchemaGenerator.Generate(typeof(ValidationException));
        context.OperationDescription.Operation.Responses.Add("400", new OpenApiResponse { Description = "An error occurs or a business rule fails.", Schema = schema });

        return true;
    }
}

配置

    services.AddOpenApiDocument(configure =>
    {
        ...
        configure.OperationProcessors.Add(new CustomResponseTypesOperationProcessor());
    });

System.InvalidOperationException: Could not resolve the path '#/paths//api/TodoItems/get/responses/400/content/application/json/schema/definitions/Exception'.
1>   at NJsonSchema.JsonReferenceResolver.ResolveDocumentReference(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver)
1>   at NJsonSchema.JsonReferenceResolver.ResolveReferenceAsync(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver, Boolean append, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonReferenceResolver.ResolveReferenceAsync(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitJsonReferenceAsync(IJsonReference reference, String path, String typeNameHint, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(Object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NJsonSchema.Infrastructure.JsonSchemaSerialization.FromJsonWithLoaderAsync[T](Func`1 loader, SchemaType schemaType, String documentPath, Func`2 referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NSwag.OpenApiDocument.FromJsonAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory, CancellationToken cancellationToken) in /_/src/NSwag.Core/OpenApiDocument.cs:line 203
1>   at NSwag.Commands.Generation.AspNetCore.AspNetCoreToSwaggerCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 239
1>   at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 275
1>   at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 81
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 85
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 32
1>   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
1>   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
1>   at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61

显然,模式是空的。

    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaginatedListOfTodoItemBriefDto"
            }
          }
        }
      },
      "400": {
        "description": "An error occurs or a business rule fails."
      }
    },

我不确定我缺少什么,也许是我试图生成该计划的方式。

I'm setting up a new API using Clean Architecture solution from here: https://github.com/jasontaylordev/CleanArchitecture

If we focus on the return types for the API.

1 - 200 - This API specification for this is generated fine (based on return type of the Action in Controller)

e.g. for get/TodoItems

"/api/TodoItems": {
  "get": {
    "tags": [
      "TodoItems"
    ],
    "operationId": "TodoItems_GetTodoItemsWithPagination",
    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaginatedListOfTodoItemBriefDto"
            }
          }
        }
      }
    },
    ]
  },

Then we have a couple of exceptions being handled in ApiExceptionFilterAttribute in the folder WebUI/Filters/

_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
            {
                { typeof(ValidationException), HandleValidationException },
                { typeof(NotFoundException), HandleNotFoundException },
                { typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
                { typeof(ForbiddenAccessException), HandleForbiddenAccessException },
            };

typeof(ValidationException) is for Http status code 400

typeof(NotFoundException) is for Http status code 404

typeof(UnauthorizedAccessException) is for Http status code 401

typeof(ForbiddenAccessException) is for Http status code 403

Now, to make these populate in the swagger.json file we will need to decorate every single controller action with the following.

[ProducesResponseType(typeof(ValidationException), 400)]
[ProducesResponseType(typeof(UnauthorizedAccessException), 401)]
[ProducesResponseType(typeof(NotFoundException), 404)]
[ProducesResponseType(typeof(ForbiddenAccessException), 403)]

To avoid this, I thought of using the IOperationProcessor interface from NSwag. but I am having trouble generating the Schema for the returned type.

public class CustomResponseTypesOperationProcessor : IOperationProcessor
{
    public bool Process(OperationProcessorContext context)
    {
        var schema = context.SchemaGenerator.Generate(typeof(ValidationException));
        context.OperationDescription.Operation.Responses.Add("400", new OpenApiResponse { Description = "An error occurs or a business rule fails.", Schema = schema });

        return true;
    }
}

In ConfigureServices

    services.AddOpenApiDocument(configure =>
    {
        ...
        configure.OperationProcessors.Add(new CustomResponseTypesOperationProcessor());
    });

But when I build the project (it should generated the swagger.json and TS API client on build, I get the following error)

System.InvalidOperationException: Could not resolve the path '#/paths//api/TodoItems/get/responses/400/content/application/json/schema/definitions/Exception'.
1>   at NJsonSchema.JsonReferenceResolver.ResolveDocumentReference(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver)
1>   at NJsonSchema.JsonReferenceResolver.ResolveReferenceAsync(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver, Boolean append, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonReferenceResolver.ResolveReferenceAsync(Object rootObject, String jsonPath, Type targetType, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitJsonReferenceAsync(IJsonReference reference, String path, String typeNameHint, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, String path, String typeNameHint, ISet`1 checkedObjects, Action`1 replacer, CancellationToken cancellationToken)
1>   at NJsonSchema.Visitors.AsyncJsonReferenceVisitorBase.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.JsonReferenceUpdater.VisitAsync(Object obj, CancellationToken cancellationToken)
1>   at NJsonSchema.JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(Object rootObject, JsonReferenceResolver referenceResolver, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NJsonSchema.Infrastructure.JsonSchemaSerialization.FromJsonWithLoaderAsync[T](Func`1 loader, SchemaType schemaType, String documentPath, Func`2 referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken)
1>   at NSwag.OpenApiDocument.FromJsonAsync(String data, String documentPath, SchemaType expectedSchemaType, Func`2 referenceResolverFactory, CancellationToken cancellationToken) in /_/src/NSwag.Core/OpenApiDocument.cs:line 203
1>   at NSwag.Commands.Generation.AspNetCore.AspNetCoreToSwaggerCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 239
1>   at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 275
1>   at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 81
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 85
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 32
1>   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
1>   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
1>   at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61

Whereas if I remove the Schema bit, the build is fine - but obviously the Schema is empty.

    "responses": {
      "200": {
        "description": "",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaginatedListOfTodoItemBriefDto"
            }
          }
        }
      },
      "400": {
        "description": "An error occurs or a business rule fails."
      }
    },

I am not sure what I'm missing, maybe it's they way I am trying to generate the Scheme.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

娇纵 2025-02-17 23:52:20

我面临同一问题,但是与您不同,因为我有几个通用控制器,其中响应类型基于通用参数而有所不同。解决方案是不要即时生成架构,并将其明确分配给结果。而是获取或生成架构并将其设置为参考。

例如:

var dtoType = typeof(FooDto);
var okResponse = new OpenApiResponse
{
    Schema = new JsonSchema()
    {
        Reference = GetSchemaForType(context, dtoType)
    }
};
context.OperationDescription.Operation.Responses.Add(StatusCodes.Status200OK.ToString(), okResponse);

在使用此方法获取或生成模式的同时:

private static JsonSchema GetSchemaForType(OperationProcessorContext context, Type type)
{
    var schema = !context.SchemaResolver.HasSchema(type, false)
        ? context.SchemaGenerator.Generate(type, context.SchemaResolver)
        : context.SchemaResolver.GetSchema(type, false);

    return schema;
}

如果您查看上面发布的JSON,则发现该模式不包括响应架构本身,而是对其进行引用。操作描述是最终被序列化的对象,因此您可以查看有效的Swagger.json,以获取有关如何在代码中正确修改它的参考。

顺便说一句:同样适用于数组类型。我也有问题为他们找到适当的解决方案,因此,如果您需要它,这也许可以帮助您。

var dtoType = typeof(FooDto);

var dtoSchema = GetSchemaForType(context, dtoType);
var okResponse = new OpenApiResponse
{
    Schema = new JsonSchema()
    {
        Type = JsonObjectType.Array,
        Item = new JsonSchema()
        {
            Reference = dtoSchema
        }
    }
};
operation.Responses.Add(StatusCodes.Status200OK.ToString(), okResponse);

I was facing the same issue, but unlike you because I am having a couple of generic controllers where the response type differs based on generic parameters. The solution is to not generate a schema on the fly and plainly assigning it to the result. Rather, get or generate the schema and set it as a reference.

As an example:

var dtoType = typeof(FooDto);
var okResponse = new OpenApiResponse
{
    Schema = new JsonSchema()
    {
        Reference = GetSchemaForType(context, dtoType)
    }
};
context.OperationDescription.Operation.Responses.Add(StatusCodes.Status200OK.ToString(), okResponse);

while using this method to get or generate the schema:

private static JsonSchema GetSchemaForType(OperationProcessorContext context, Type type)
{
    var schema = !context.SchemaResolver.HasSchema(type, false)
        ? context.SchemaGenerator.Generate(type, context.SchemaResolver)
        : context.SchemaResolver.GetSchema(type, false);

    return schema;
}

If you look at the json you posted above you see that the schema does not include the response schema itself but a reference to it. The OperationDescription is the object that ends up being serialized so you can look at a valid swagger.json to get a reference on how to properly modify it in code.

Btw: same applies to array types. I had issues finding a proper solution for them as well so maybe this helps you out in case you need it.

var dtoType = typeof(FooDto);

var dtoSchema = GetSchemaForType(context, dtoType);
var okResponse = new OpenApiResponse
{
    Schema = new JsonSchema()
    {
        Type = JsonObjectType.Array,
        Item = new JsonSchema()
        {
            Reference = dtoSchema
        }
    }
};
operation.Responses.Add(StatusCodes.Status200OK.ToString(), okResponse);
挽你眉间 2025-02-17 23:52:20

我遇到了同样的问题,但是公认的解决方案对我不起作用。

经过很多调试后,我发现了这个问题(至少在我的情况下)。

schemageNerator.generate(...)generateWithReference(...)或 generateWithReenceAndNullability(...) benate(...)。

我最终得到了类似的东西:

using Namotion.Reflection; // needed for .ToContextualType()

var schema = context.SchemaGenerator.GenerateWithReference<JsonSchema>(
    typeof(ValidationException).ToContextualType(), 
    context.SchemaResolver);

I had the same issue but the accepted solution didn't work for me.

After a lot of debugging I figured out the issue (at least in my case).

Replacing the use of SchemaGenerator.Generate(...) with either GenerateWithReference(...) or GenerateWithReferenceAndNullability(...).

I ended up with something similar to this:

using Namotion.Reflection; // needed for .ToContextualType()

var schema = context.SchemaGenerator.GenerateWithReference<JsonSchema>(
    typeof(ValidationException).ToContextualType(), 
    context.SchemaResolver);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文