Swashbuckle 使 asp.net core 3.1 api swagger ui 中具有相同动词但多个路由的端点的所有路由参数都是强制性的
Swashbuckle makes all route parameter mandatory for endpoints with same verb but multiple routes in 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
:
配置服务:
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();
});
生成的 swagger 使得 anotherId
在两条路由中都是强制性的。以前不是这样的。我尝试将 Name
添加到两条路线,但仍然失败。请协助我哪里错了。
经过一些分析后,我使用 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>();
};
我不确定这是最好的方法,但它确实有效。如有错误请指正
我正在处理 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
:
配置服务:
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();
});
生成的 swagger 使得 anotherId
在两条路由中都是强制性的。以前不是这样的。我尝试将 Name
添加到两条路线,但仍然失败。请协助我哪里错了。
经过一些分析后,我使用 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>();
};
我不确定这是最好的方法,但它确实有效。如有错误请指正