Swashbuckle делает все параметры маршрута обязательными для конечных точек с одним и тем же глаголом, но с несколькими маршрутами в asp. net core 3.1 api swagger ui - PullRequest
0 голосов
/ 16 марта 2020

Я работаю над проектом asp.net core 2.2 и обновляюсь до asp.net core 3.1, а также обновляю Swashbuckle.AspNetCore до 5.0.0. После обновления я вижу изменения в генерируемых конечными точками червя.

У меня есть конечная точка для [HttpDelete] с двумя различными маршрутами, как показано ниже:

[HttpDelete("{id}")]
[HttpDelete("{id}/some/{anotherId}")]
public IActionResult Delete(int id, int anotherId) 
{
    return NoContent();
}

[HttpDelete("{id}")]

Здесь требуется только параметр id. Но оба параметра id и anotherId также отмечены здесь как обязательные. Это неправильно .

enter image description here

[HttpDelete("{id}/some/{anotherId}")]

Оба id и anotherId параметр должен быть обязательным здесь. Это правильно.

enter image description here

Вот мой Startup.cs:

ConfigureServices:

services.AddVersionedApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VV";
});

services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
    options.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
});

var apiVersionDescriptionProvider =
    services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();

services
    .AddSwaggerGen(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerDoc(
            $"TestDocumentOpenAPISpecification{description.GroupName}",
            new Microsoft.OpenApi.Models.OpenApiInfo
            {
                Title = "Test Document API",
                Version = description.ApiVersion.ToString(),
                Description = "Test",
                Contact = new Microsoft.OpenApi.Models.OpenApiContact
                {
                    Email = "Test@test.com",
                    Name = "Test Team",
                    Url = new Uri("https://www.test.com")
                }
            });
    }

    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "Input your JWT Authorization header to access this API. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] { }
        }
    });

    options.DocInclusionPredicate((documentName, apiDescription) =>
    {
        var actionApiVersionModel = apiDescription.ActionDescriptor
        .GetApiVersionModel(ApiVersionMapping.Explicit | ApiVersionMapping.Implicit);

        if (actionApiVersionModel == null)
        {
            return true;
        }

        if (actionApiVersionModel.DeclaredApiVersions.Any())
        {
            return actionApiVersionModel.DeclaredApiVersions.Any(v =>
            $"TestDocumentOpenAPISpecificationv{v.ToString()}" == documentName);
        }

        return actionApiVersionModel.ImplementedApiVersions.Any(v =>
            $"TestDocumentOpenAPISpecificationv{v.ToString()}" == documentName);
    });

    //var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    //var xmlCommentsFullPath = Path.Combine(AppContext.BaseDirectory, xmlCommentsFile);

    //options.IncludeXmlComments(xmlCommentsFullPath);
});

Сконфигурировать:

app.UseSwagger();

app.UseSwaggerUI(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerEndpoint(
            $"/swagger/TestDocumentOpenAPISpecification{description.GroupName}/swagger.json",
            $"Test Document API - {description.GroupName.ToUpperInvariant()}");
    }
    options.RoutePrefix = string.Empty;

    options.DefaultModelExpandDepth(2);
    options.DefaultModelRendering(Swashbuckle.AspNetCore.SwaggerUI.ModelRendering.Model);
    options.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
    options.DisplayRequestDuration();
    options.EnableValidator();
    options.EnableFilter();
    options.EnableDeepLinking();
    options.DisplayOperationId();
});

Сгенерированный чванство делает anotherId обязательным на обоих маршрутах. Это было не так раньше. Я попытался добавить Name к обоим маршрутам, но все равно не получается. Пожалуйста, помогите, где я не прав.

1 Ответ

0 голосов
/ 17 марта 2020

После некоторого анализа я заставил это работать, используя IOperationFilter.

public class DeleteOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (context.ApiDescription.HttpMethod == "DELETE" && context.MethodInfo.Name == "Delete")
        {
            foreach (var parameter in context.ApiDescription.ParameterDescriptions)
            {
                if (parameter.RouteInfo == null)
                {
                    operation.Parameters.Single(x => x.Name.Equals(parameter.Name)).Required = false;
                }
            }
            return;
        }
    }
}

И добавив его в ConfigureServices,

services.AddSwaggerGen(options =>
{
    ...
    options.OperationFilter<DeleteOperationFilter>();
};

Не уверен, что это лучший способ, но это работает. Пожалуйста, поправьте меня, если я ошибаюсь.

...