简体   繁体   English

在 asp.net core 3.1 web api 中,分组和版本控制不能很好地协同工作

[英]Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api

I'm using Asp.Net Core 3.1 to build my API.我正在使用Asp.Net Core 3.1来构建我的 API。 I'm using swagger to generate document for my API.我正在使用 swagger 为我的 API 生成文档。 I decided to do grouping on my swagger document based on controller.我决定根据控制器对我的招摇文档进行分组。 So I ended up doing like this,所以我最终这样做了,

Startup - ConfigureServices:启动 - 配置服务:

options.SwaggerDoc(
    "LibraryOpenAPISpecificationCategories",
    ...

Startup - Configure:启动 - 配置:

options.SwaggerEndpoint(
    "/swagger/LibraryOpenAPISpecificationCategories/swagger.json",
    "Library API (Categories)");

Controller:控制器:

[Route("api/categories")]
[ApiController]
[ApiExplorerSettings(GroupName = "LibraryOpenAPISpecificationCategories")]
public class CategoriesController : ControllerBase

Until this point everything was working fine.到目前为止,一切都运行良好。 When I added versioning the Swagger document stopped displaying the methods in the controller.当我添加版本控制时,Swagger 文档停止显示控制器中的方法。 I was trying to bring grouping inside version so that each version will have the groups like,我试图在版本内部进行分组,以便每个版本都有这样的组,

V1 -> LibraryOpenAPISpecificationCategories V1 -> 库OpenAPISpecificationCategories

V1 -> LibraryOpenAPISpecificationItems V1 -> 库OpenAPISpecificationItems

V2 -> LibraryOpenAPISpecificationCategories V2 -> 库OpenAPISpecificationCategories

V2 -> LibraryOpenAPISpecificationItems V2 -> 库OpenAPISpecificationItems

Here is what I did,这是我所做的,

Startup - ConfigureServices:启动 - 配置服务:

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

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

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

services.AddSwaggerGen(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerDoc(
            $"LibraryOpenAPISpecificationCategories{description.GroupName}",
            ...

Startup - Configure:启动 - 配置:

app.UseSwaggerUI(options =>
{
    foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
    {
        options.SwaggerEndpoint(
            $"/swagger/LibraryOpenAPISpecificationCategories{description.GroupName}/swagger.json",
            $"Library API (Categories) {description.GroupName.ToUpperInvariant()}");

Controller:控制器:

[Route("api/categories")]
[ApiController]
[ApiExplorerSettings(GroupName = "LibraryOpenAPISpecificationCategories")]
public class CategoriesController : ControllerBase

No error is displayed in swagger document. swagger 文档中没有显示错误。 Please assist me on where I'm going wrong.请帮助我解决我哪里出错了。 Am I missing anything?我错过了什么吗?

After some analysis, I figured out that I missed DocInclusionPredicate in AddSwaggerGen in my ConfigureServices .经过一番分析,我发现我在ConfigureServices中错过了DocInclusionPredicate中的AddSwaggerGen

Here is how I resolved,这是我的解决方法,

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

    var apiExplorerSettingsAttribute = (ApiExplorerSettingsAttribute)apiDescription.ActionDescriptor.EndpointMetadata.First(x => x.GetType().Equals(typeof(ApiExplorerSettingsAttribute)));

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

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

Hope this helps someone out there.希望这可以帮助那里的人。

Since a few people asked for this in various places, here's how you'd implement a custom IApiDescriptionProvider .由于一些人在各个地方都要求这样做,因此您将如何实现自定义IApiDescriptionProvider It simply updates the ApiDescription.GroupName at the end of processing.它只是在处理结束时更新ApiDescription.GroupName This will work completely independent of Swashbuckle or any other OpenAPI/Swagger document generator:这将完全独立于 Swashbuckle 或任何其他 OpenAPI/Swagger 文档生成器:

public class CollateApiDescriptionProvider : IApiDescriptionProvider
{
    readonly IOptions<ApiExplorerOptions> options;

    public CollateApiDescriptionProvider( IOptions<ApiExplorerOptions> options ) =>
        this.options = options;

    public int Order => 0;

    public void OnProvidersExecuting( ApiDescriptionProviderContext context ) { }

    public void OnProvidersExecuted( ApiDescriptionProviderContext context )
    {
        var results = context.Results;
        var format = options.Value.GroupNameFormat;
        var text = new StringBuilder();

        for ( var i = 0; i < results.Count; i++ )
        {
            var result = results[i];
            var action = result.ActionDescriptor;
            var version = result.GetApiVersion();
            var groupName = action.GetProperty<ApiDescriptionActionData>()?.GroupName;

            text.Clear();

            // add the formatted API version according to the configuration
            text.Append( version.ToString( format, null ) )

            // if there's a group name, prepend it
            if ( !string.IsNullOrEmpty( groupName ) )
            {
                text.Insert( 0, ' ' );
                text.Insert( 0, groupName );
            }

            result.GroupName = text.ToString();
        }
    }
}

To register your new provider, add it to the service collection:要注册您的新提供者,请将其添加到服务集合中:

services.TryAddEnumerable(
    ServiceDescriptor.Transient<IApiDescriptionProvider, CollateApiDescriptionProvider>() );

NOTE: this should occur after services.AddApiVersioning()注意:这应该发生services.AddApiVersioning()之后

You can get as creative as you want with the group names, but be aware that you cannot create multiple levels of grouping.您可以根据需要使用组名称来获得创意,但请注意,您无法创建多个级别的分组。 It simply isn't supported out-of-the-box.它根本不支持开箱即用。 In most cases, you can only have a single OpenAPI/Swagger document per API version.在大多数情况下,每个 API 版本只能有一个 OpenAPI/Swagger 文档。 This is because URLs must be unique within the document.这是因为 URL 在文档中必须是唯一的。

It is technically possible to group on multiple levels, but this would require a number of changes to the UI and document generation process.技术上讲,可以在多个级别上进行分组,但这需要对 UI 和文档生成过程进行大量更改。 I've only ever seen a handful of people willing to put that much effort in. They effectively created their own UIs and documentation generation backend.我只见过少数愿意付出这么多努力的人。他们有效地创建了自己的 UI 和文档生成后端。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM