NSwag: создание клиента C # из нескольких версий API

Мы обновляем наш API и генерируем спецификацию Swagger с помощью Swashbuckle в ASP.NET Core 1.1. Мы можем сгенерировать два документа API на основе этих файлов спецификации JSON:

<!-- language: c# -->
services.AddSwaggerGen(setupAction =>
{
    setupAction.SwaggerDoc("0.1", new Info { Title = "Api", Version = "0.1", Description = "API v0.1" });
    setupAction.SwaggerDoc("0.2", new Info { Title = "Api", Version = "0.2", Description = "API v0.2" });

    // more configuration omitted
}

Мы включаем все действия в оба файла спецификаций, если они не сопоставлены с конкретной версией с помощью атрибутов [MapToApiVersion] и ApiExplorerSettings(GroupName ="<version>")]. Методы, принадлежащие только более старой версии, также украшены атрибутом [Obsolete]:

<!-- language: c# -->
[MapToApiVersion("0.1")]
[ApiExplorerSettings(GroupName = "0.1")]
[Obsolete]

Однако мы хотим иметь только один клиент C #, сгенерированный из объединения обоих файлов спецификации, где все методы включены в клиент, 0,1 и 0,2, но все устаревшие методы помечены как устаревшие.

Я изучил как NSwag (который мы используем уже довольно давно), так и AutoRest. AutoRest, похоже, поддерживает сценарий слияния, но мне не удалось заставить его работать из-за ошибок проверки схемы (и я более чем не уверен, будет ли на самом деле поддерживаться наш конкретный сценарий).

Моя последняя идея на данный момент, чтобы отсортировать это, - это каким-то образом JSON-объединить спецификации в одну, а затем передать ее в NSwag.

Мы что-то здесь упускаем? Возможно ли это как-то реализовать с помощью NSwag?


person Structed    schedule 30.05.2018    source источник
comment
Итак, если я правильно понимаю, в настоящее время вы используете Swashbuckle и хотите перейти на NSwag / AutoRest?   -  person Baksteen    schedule 30.05.2018
comment
Нет. Мы создаем спецификацию (JSON) с помощью Swashbuckle.AspNetCore и создаем клиент C # с помощью NSwag. Мы не хотим ничего переносить, если это возможно.   -  person Structed    schedule 30.05.2018
comment
Думаю, вы можете сделать это в Swashbuckle, взгляните на этот: swagger-net-test-multiapiversions.azurewebsites.net/swagger/ui/   -  person Helder Sepulveda    schedule 01.06.2018
comment
@HelderSepu, это похоже на то, что мне нужно. Я думаю, что по умолчанию я затем сгенерирую клиента. У вас есть ссылка на конкретную документацию или пример кода? Спасибо!   -  person Structed    schedule 01.06.2018


Ответы (3)


Я написал статью о подобной проблеме https://medium.com/dev-genius/nswag-charp-client-from-multiple-api-versions-7c79a3de4622

Прежде всего, создайте схему. Как я вижу, есть два подхода:

  • одна схема, в которой живут несколько версий
  • собственная схема для каждой версии

Затем создайте клиентов для каждой поддерживаемой версии и оберните их в оболочку client:

public class AppApiClient
{
    public IV1Client V1 { get; }
    public IV2Client V2 { get; }

    public AppApiClient(HttpClient httpClient)
    {
        V1 = new V1Client(httpClient);
        V2 = new V2Client(httpClient);
    }
}
person Renat Sungatullin    schedule 14.07.2020
comment
Хотя моя проблема больше не актуальна, я чувствую, что это именно то, что мне нужно. Спасибо Вам большое! - person Structed; 15.07.2020

Вот моя идея, расширяющаяся из комментариев:

С помощью swashbuckle вы можете сгенерировать столько SwaggerDoc, сколько захотите, идея в этом случае состоит в том, чтобы сгенерировать 3, сохранить те же 2 версии, которые у вас есть, и добавить еще одну, в которой будет все.

c.MultipleApiVersions(
    (apiDesc, targetApiVersion) => 
      targetApiVersion.Equals("default") || // Include everything by default
      apiDesc.Route.RouteTemplate.StartsWith(targetApiVersion), // Only include matching routes for other versions
    (vc) =>
    {
        vc.Version("default", "Swagger_Test");
        vc.Version("v1_0", "Swagger_Test V1_0");
        vc.Version("v2_0", "Swagger_Test V2_0");
    });

Вот рабочий пример:
http://swagger-net-test-multiapiversions.azurewebsites.net/swagger/ui/index?filter=Api

И весь код этого проекта находится на GitHub:
https://github.com/heldersepu/Swagger-Net-Test/tree/MultiApiVersions

person Helder Sepulveda    schedule 01.06.2018
comment
Спасибо! Однако ваш пример, похоже, из Swashbuckle для полной .NET Framework, но я изучал ASP.NET Core с Swashbuckle.ASpNetCore. Здесь я не могу воспроизвести ваше решение. - person Structed; 04.06.2018

Пакеты:

Установочный пакет Swashbuckle.AspNetCore

Установочный пакет Microsoft.AspNetCore.Mvc.Versioning

введите описание изображения здесь

ValueV1Controller.cs

[ApiVersion("1")]
[Route("api/v{version:apiVersion}/Values")]
public class ValuesV1Controller : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

ValueV2Controller.cs

[ApiVersion("2")]
[Route("api/v{version:apiVersion}/Values")]
public class ValuesV2Controller : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1.2", "value2.2" };
    }
}

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddApiVersioning();
        // Register the Swagger generator, defining 1 or more Swagger documents
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new Info { Title = "My API - V1", Version = "v1" });
            c.SwaggerDoc("v2", new Info { Title = "My API - V2", Version = "v2" });

            c.DocInclusionPredicate((docName, apiDesc) =>
            {
                var versions = apiDesc.ControllerAttributes()
                    .OfType<ApiVersionAttribute>()
                    .SelectMany(attr => attr.Versions);

                return versions.Any(v => $"v{v.ToString()}" == docName);
            });

            c.OperationFilter<RemoveVersionParameters>();
            c.DocumentFilter<SetVersionInPaths>();
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger();

        // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
        // specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v2/swagger.json", "My API V2");
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        });

        app.UseMvc();
    }
}

public class RemoveVersionParameters : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var versionParameter = operation.Parameters?.SingleOrDefault(p => p.Name == "version");
        if (versionParameter != null)
            operation.Parameters.Remove(versionParameter);
    }
}

public class SetVersionInPaths : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    {
        swaggerDoc.Paths = swaggerDoc.Paths
            .ToDictionary(
                path => path.Key.Replace("v{version}", swaggerDoc.Info.Version),
                path => path.Value
            );
    }
}
person Narottam Goyal    schedule 01.07.2018
comment
У меня это работает, с некоторыми изменениями для Core 3.1. Эта ссылка помогла мне с изменениями 3.1. (см. комментарии на странице) dev.to/htissink/ - Кроме того, мне не нужно было использовать DocInclusionPredicate, так как у меня все работало без него. Он фактически порвал с ней. - person bugnuker; 26.04.2021