.NET Core 3.1中Swagger API版本冲突的命名空间URL问题

4

我正在使用swagger生成我的API文档,现在我需要对一些终端点进行版本控制。

因此,我配置了swagger来识别我的版本并正确映射终端点。但是,swagger会失去追踪,因为我在不同的命名空间中使用了相同的类名,我收到这个错误:

Conflicting method/path combination "GET api/v1/A" for actions - TesteSwagger.Controllers.B.AController.x (TesteSwagger),TesteSwagger.Controllers.A.AController.x (TesteSwagger). Actions require a unique method/path combination for Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround

以下是我制作的示例以复现它:

我所有的 swagger 包都是 6.0.2 版本。

我正在使用 .Net Core 3.1 WebApi 默认空模板。

Startup.cs:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddApiVersioning(
                options => 
                {
                    options.ReportApiVersions = true;
                    options.DefaultApiVersion = new ApiVersion(1, 0);
                    options.AssumeDefaultVersionWhenUnspecified = true;
                });

            services.AddVersionedApiExplorer(
                options =>
                {
                    options.GroupNameFormat = "'v'VVV";
                    options.SubstituteApiVersionInUrl = true;
                });

            services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();

            services.AddSwaggerGen(options =>
            {
                options.CustomSchemaIds(x => x.FullName);
                options.DescribeAllParametersInCamelCase();
                options.OperationFilter<SwaggerDefaultValues>();
                options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseSwagger()
              .UseSwaggerUI(c =>
              {
                  c.DisplayRequestDuration();
                  foreach (var description in provider.ApiVersionDescriptions)
                  {
                      c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
                          description.GroupName.ToUpperInvariant());
                  }
              });

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

Swagger 默认配置类:

    public class SwaggerDefaultValues : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            
        }
    }

    public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
    {
        public void Configure(SwaggerGenOptions options)
        {
        }
    }

控制器(v1):

namespace TesteSwagger.Controllers.A
{
    [ApiController, ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class AController : ControllerBase
    {
        [HttpPost]
        [ProducesResponseType(typeof(B), (int)HttpStatusCode.OK)]
        public IActionResult x(A a) => Ok(new B());
    }

    public class A
    {
        public int Foo { get; set; }
    }

    public class B
    {
        public int Bar { get; set; }
    }
}

B控制器(v2):

namespace TesteSwagger.Controllers.B
{
    [ApiController, ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    public class AController : ControllerBase
    {
        [HttpPost]
        [ProducesResponseType(typeof(B), (int)HttpStatusCode.OK)]
        public IActionResult x(A a) => Ok(new B());
    }

    public class A
    {
        public int Foo { get; set; }
    }

    public class B
    {
        public int Bar { get; set; }
    }
}

Swagger的v1 URL可以正常加载,只有当我将其更改为v2时,才会在屏幕上显示此错误:

Fetch error
undefined /swagger/v2/swagger.json

我使用在v1 URL生成的curl进行测试,一切都很顺利,只是swagger无法理解。

// works great
curl -X POST "https://localhost:44312/api/v1/A" -H  "accept: text/plain" -H  "Content-Type: application/json" -d "{\"foo\":0}"

// works great
curl -X POST "https://localhost:44312/api/v2/A" -H  "accept: text/plain" -H  "Content-Type: application/json" -d "{\"foo\":0}"

我真的不知道我做错了什么还是Swagger确实不支持这种类型的版本控制。

有什么想法吗?


我认为你有一个命名空间TesteSwagger.Controllers.B和一个类B,这很令人困惑。错误消息说,不要使用ProducesResponseType(typeof(B)),而是尝试使用ProducesResponseType(typeof(TesteSwagger.Controllers.B.B))。 - jdweng
无法工作,我在两个控制器中都完全限定了参数和响应,但问题仍然存在。 - MestreDosMagros
错误信息表明您需要使用完整名称(包括命名空间)。不清楚需要更改哪个对象。我只猜测是B类。可能还有其他类似的需要更改。 - jdweng
这是一个干净的项目,仅用于模拟错误,没有其他对象。这些是我的解决方案中唯一的两个对象和唯一的控制器。 - MestreDosMagros
你尝试过使用完整的名称来调用 "x(A a) => Ok(new B())" 吗?我不知道 Swagger 是如何将字符串名称转换为类类型的,但显然它不喜欢带有点号后跟 "B" 的命名空间。 - jdweng
显示剩余2条评论
1个回答

1
首先,类型名称通常是不相关的。 命名空间不是可以通过HTTP传递的概念,并且高度依赖于媒体类型。 现代JavaScript确实具有模块,但它并不完全是命名空间。 不管怎样,.NET命名空间都不会被转换为一组JavaScript模块以在JSON中表示; 当然,默认情况下不会这样做。
这里的系统问题似乎是您定义了一个没有任何实际配置的Swashbuckle配置。 所有这些部分需要三个方面相互配合才能形成整体:
  1. 添加版本化API浏览器 - 提供API版本扩展,按照格式化的API版本将其汇总成组,类似于ApiDescription.GroupName。在AddVersionedApiExplorer中的配置有助于在通过URL段进行版本控制时对典型的Swashbuckle格式和示例进行对齐。
  2. 添加Swagger端点 - 通过UseSwaggerUI实现。每个API版本通常预期有一个端点。段值通常映射到API版本组名称(例如格式化版本)
  3. 添加Swagger文档 - 配置Swashbuckle以定义每个API版本的Open API(以前是Swagger)文档。文档的键映射到#2中定义的端点的ApiDescription.GroupName和片段。

您没有提供#3的任何实现或配置。因此,Swashbuckle假定只有一个文档。由于您具有相同名称的模型在同一文档中,因此会出现文档生成错误。您不能在同一文档中具有重复的路由路径或模型名称。但是,当您为每个API版本定义文档时,可以跨多个版本具有重复的模型名称。

注意:在同一API版本中可能存在重复的.NET类型名称,但您必须提供别名。命名空间永远不会被考虑为不同之处。除非真的没有意义,否则我建议您在每组版本化API中使用唯一的类型/类作为模型名称。

ASP.NET API版本控制Swagger样例演示了总体配置应该是什么样子。一个典型的配置将如下所示:

public void Configure( SwaggerGenOptions options )
{
    // configure Swashbuckle to define a Swagger document per defined API version
    foreach ( var description in provider.ApiVersionDescriptions )
    {
        options.SwaggerDoc(
            description.GroupName,
            new OpenApiInfo()
            {
                Title = "API " + description.GroupName,
                Version = description.ApiVersion.ToString(),
            } );
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接