Swashbuckle делает все параметры маршрута обязательными для конечных точек с одним и тем же глаголом, но с несколькими маршрутами в asp.net core 3.1 api swagger ui
Я работаю на asp.net core 2.2
проект и обновление до asp.net core 3.1
а также обновление Swashbuckle.AspNetCore
к 5.0.0
. После обновления я вижу изменения в конечных точках, сгенерированных swagger.
У меня есть конечная точка для [HttpDelete]
с двумя разными маршрутами, как показано ниже:
[HttpDelete("{id}")]
[HttpDelete("{id}/some/{anotherId}")]
public IActionResult Delete(int id, int anotherId)
{
return NoContent();
}
[HttpDelete("{id}")]
Только id
параметр здесь должен быть обязательным. Но обаid
а также anotherId
здесь также помечены как обязательные. Это неправильно.
[HttpDelete("{id}/some/{anotherId}")]
Обе id
а также anotherId
параметр здесь должен быть обязательным. Это правильно.
Вот мой 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 ответ
После некоторого анализа я заставил это работать, используя 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>();
};
Не уверен, что это лучший способ, но это работает. Пожалуйста, поправьте меня, если я ошибаюсь.