swagger ASP.NET Core [FromBody]和Blazor中的最小API意外返回值

nuypyhwy  于 2024-01-07  发布在  .NET
关注(0)|答案(1)|浏览(144)

我在这里有我的最小API设置与ASP.NET核心版本和卡特。PutMethodPostMethod,和PatchMethod工作正常,但我的GetMethodDeleteMethod不返回字符串。
这是我的API版本和最小API:

public class ApiVersions
{
    public const string vset = "API_Version_Set";
    public static ApiVersion v1 = new ApiVersion(1);
    public static ApiVersion v2 = new ApiVersion(2);
    public static ApiVersion v3 = new ApiVersion(3);
}

个字符
这是我在Program.cs上的配置:

builder.Services.AddCarter()
    .AddEndpointsApiExplorer()
    .AddApiVersioning(options =>
{
    options.ReportApiVersions = true;
    options.DefaultApiVersion = ApiVersions.v1;
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = new UrlSegmentApiVersionReader();
}).AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
});

app.MapCarter().NewApiVersionSet(ApiVersions.vset)
    .HasApiVersion(ApiVersions.v1)
    .HasApiVersion(ApiVersions.v2)
    .HasApiVersion(ApiVersions.v3)
    .ReportApiVersions()
    .Build();


GetMethodDeleteMethod只有在我在代码中配置一些行时才能正常工作:
方法1:如果我更改了GetMethodDeleteMethod版本:

group.MapGet("{url}", GetMethod).HasApiVersion(ApiVersions.v2);
group.MapGet("{url}", DeleteMethod).HasApiVersion(ApiVersions.v2);


方法2:如果我使用[FromRoute]并删除PutMethodPostMethodPatchMethod中的LoginDTO参数:

public static string GetMethod([FromRoute] string url) => "This is from GET";
public static string DeleteMethod([FromRoute] string url) => "this is from DELETE.";
public static string PutMethod([FromRoute] string url) => "this is from PUT";
public static string PostMethod([FromRoute] string url) => "this is from POST.";
public static string PatchMethod([FromRoute] string url) => "this is from PATCH.";


方法3:如果我改变路径:

group.MapPut("put/{url}", PutMethod).HasApiVersion(ApiVersions.v1);
group.MapPost("post/{url}", PostMethod).HasApiVersion(ApiVersions.v1);
group.MapPatch("patch/{url}", PatchMethod).HasApiVersion(ApiVersions.v1);


方法4:如果我评论这三个:

//group.MapPut("{url}", PutMethod).HasApiVersion(ApiVersions.v1);
//group.MapPost("{url}", PostMethod).HasApiVersion(ApiVersions.v1);
//group.MapPatch("{url}", PatchMethod).HasApiVersion(ApiVersions.v1);

w8f9ii69

w8f9ii691#

我 * 认为 * 我能够重现你的场景。根据你的描述,看起来事情正在 * 工作 *,但它们没有显示在Swagger UI中。你没有分享你的配置的那一部分,但我猜它是不正确的。你没有指定或指出你正在使用哪个OpenAPI生成器,但我假设它是Swashbuckle。有很多事情正在发生,让我们一个一个来解决。

问题1 -路由参数未定义

您定义了路由模板:

group.MapPost("{url}", PostMethod).HasApiVersion(ApiVersions.v1);

字符串
但方法:

public static string PostMethod(LoginDTO login) => "this is from POST.";

noturl参数。这将导致文本{url}成为路由路径的一部分。

Issue 2 - OpenAPI单据生成

Swashbuckle将急切地评估AddSwaggerGen中的设置,因此我们需要使用 Options 通过DI进行不同的解析。我们希望使用从API Versioning中解析IApiVersionDescriptorProvider,这将整理应用程序中的所有API版本。这将使您能够配置OpenAPI文档,而无需硬编码任何内容。世界上最简单的实现如下所示:

public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider provider;

    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
        => this.provider = provider;

    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(
                description.GroupName,
                new()
                {
                    Title = "Example API",
                    Description = "An example application with OpenAPI, " +
                                  "Swashbuckle, and API versioning.",
                    Version = description.ApiVersion.ToString(),
                });
        }
    }
}

Issue 3 - API版本元数据

您仍然可以显式地使用旧的ApiVersionSet API,但是在端点和/或其组上隐式地配置API版本可能更自然。在引擎盖下总是有一个ApiVersionSet。与ApiVersionSet关联的名称是API的逻辑名称,默认情况下将在您集成OpenAPI的API Explorer扩展时使用。
API版本元数据必须从根组上滚。当您使用组和端点扩展方法时,将严格执行该行为。当您手动构建ApiVersionSet时,只能松散地执行规则,并且出现错误的可能性更大。您可能希望使用两种方法进行配置。还有其他选项,但你可能会对这些感兴趣

选项1 -带嵌套组的通用路由模板

此方法在根目录为所有API版本配置路由模板。每组端点必须汇总到一个组以进行正确的排序,因此我们需要添加一个新组(不带路由模板)来表示端点集合。通过在组上应用API版本,该组中的所有端点都将具有父组定义的API版本。

public class GeneralController : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        var general = app.NewVersionedApi("General")
                         .MapGroup("api/v{version:apiVersion}/general");

        var v1 = general.MapGroup("").HasApiVersion(ApiVersions.v1);

        v1.MapGet("{url}", GetMethod);
        v1.MapDelete("{url}", DeleteMethod);
        v1.MapPut("{url}", PutMethod);
        v1.MapPost("/", PostMethod);
        v1.MapPatch("{url}", PatchMethod);

        var v2 = general.MapGroup("").HasApiVersion(ApiVersions.v2);

        v2.MapGet("{url}", GetMethod);
        v2.MapDelete("{url}", DeleteMethod);
    }
}

选项2 -使用重复路由模板的组

在此方法中,每个组中的路由模板会针对每个API版本进行复制。这可能对您有用,也可能不适用。不同的API版本可能使用不同的路由模板。同样,将API版本添加到组中会隐式地将API版本应用于该组中的所有端点。

public class GeneralController : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        var general = app.NewVersionedApi("General");

        var v1 = general.MapGroup("api/v{version:apiVersion}/general")
                        .HasApiVersion(ApiVersions.v1);

        v1.MapGet("{url}", GetMethod);
        v1.MapDelete("{url}", DeleteMethod);
        v1.MapPut("{url}", PutMethod);
        v1.MapPost("/", PostMethod);
        v1.MapPatch("{url}", PatchMethod);

        var v2 = general.MapGroup("api/v{version:apiVersion}/general")
                        .HasApiVersion(ApiVersions.v2);

        v2.MapGet("{url}", GetMethod);
        v2.MapDelete("{url}", DeleteMethod);
    }
}

问题4 - OpenAPI扩展冲突

Microsoft OpenAPI扩展与API Versioning API Explorer扩展不兼容。具体来说,当您使用WithOpenApi()时,它会将OpenApiOperation直接添加到端点作为元数据。这是设计中的一个缺陷。端点可能会使用不同的OpenAPI选项来服务多个API版本。不幸的是,OpenAPI生成器,如Swashbuckle,在元数据中查找OpenApiOoperation的存在。当存在时,它使用该示例并跳过API Explorer提供的所有其他内容。这可能会导致您不想要的结果。
如果您使用的是API版本控制,我建议您不要使用WithOpenApi()。您可以使用其他影响OpenAPI的扩展,例如WithSummaryWithDescriptionAcceptsProduces,大多数元数据扩展方法只添加元数据,您不需要使用OpenApiOperation,使用WithTags,因为使用ApiVersionSet配置的名称是默认使用的名称。不需要定义两次,但如果你想,你可以。

问题5 -应用配置

您的应用程序配置不需要重新应用ApiVersionSet。它已经通过Carter的端点配置应用。将所有部分放在一起,您的应用程序配置应该如下所示:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddCarter()
    .AddEndpointsApiExplorer()
    .AddApiVersioning(options =>
    {
        options.ReportApiVersions = true;
        options.DefaultApiVersion = ApiVersions.v1;
        options.ApiVersionReader = new UrlSegmentApiVersionReader();
    })
    .AddApiExplorer(options =>
    {
        options.GroupNameFormat = "'v'VVV";
        options.SubstituteApiVersionInUrl = true;
    });

builder.Services
       .AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.MapCarter();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(
        options =>
        {
            var descriptions = app.DescribeApiVersions();

            foreach (var description in descriptions)
            {
                var url = $"/swagger/{description.GroupName}/swagger.json";
                var name = description.GroupName.ToUpperInvariant();
                options.SwaggerEndpoint(url, name);
            }
        });

}

app.Run();


这里的一个关键问题是如何调用UseSwaggerUI。这个设置需要与ConfigureSwaggerOptions类中定义的OpenAPI文档的创建方式相关联。双方需要匹配。
最后,您可能已经注意到我删除了UseDefaultVersionWhenUnspecified。这几乎肯定不会像您认为的那样;至少,不是如图所示。此功能用于向后兼容现有API,但经常被滥用。您正在通过URL段进行版本控制,所以该选项无效。模板中间不能有可选路由参数。order/{id}/items也是如此。该选项将如果你也有不包含apiVersion路由约束的路由(例如:api/general/{url}),我不建议你在向后兼容性之外做任何事情。

相关问题