Другое

Ошибка No operations defined in spec в MMLibSwagger

Устраните ошибку 'No operations defined in spec' при сборке комбинированного Swagger для Ocelot с помощью MMLibSwagger. Узнайте настройки, решения и лучшие практики. Пошаговые рекомендации и проверка конфигурации.

Как исправить ошибку “No operations defined in spec!” при использовании MMLib.SwaggerForOcelot для построения объединённого swagger‑документа Ocelot?

У меня возникает проблема: шлюз Ocelot корректно маршрутизирует запросы, но объединённый swagger показывает “No operations defined in spec!”, хотя спецификация downstream‑swagger валидна.

Конфигурация Ocelot

json
{
  "Routes": [
    {
      "UpstreamPathTemplate": "/api/test/{url}",
      "DownstreamPathTemplate": "/api/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "test-host",
          "Port": 8080
        }
      ],
      "SwaggerKey": "test"
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "test",
      "Config": [
        {
          "Name": "Test API",
          "Version": "v1",
          "Url": "http://test-host:8080/swagger/v1/swagger.json"
        }
      ]
    }
  ]
}

Детали окружения

  • Downstream‑эндпоинт – работает в контейнере без проброса внешнего порта, доступен внутренне по http://test-host:8080/api/weather-forecast.
  • Downstream swagger – доступен по http://test-host:8080/swagger с рабочими эндпоинтами.
  • Upstream‑эндпоинт – работает в другом контейнере, имеет доступ к downstream, пробрасывается на localhost:80.
  • Upstream‑маршрутизацияhttp://localhost/api/test/weather-forecast возвращает ожидаемые результаты.
  • Объединённый swagger – доступен по http://localhost/swagger/; он обнаруживает downstream‑swagger (показывает “Test API” в выпадающем списке), но не отображает эндпоинты.

Шаги устранения неполадок

  1. Подтверждена корректность загрузки downstream‑swagger (модели рендерятся при RemoveUnusedComponentsFromScheme: false).
  2. Подтверждена доступность спецификации (пр intentionally breaking the URL returns an error).
  3. При TransformByOcelotConfig: false эндпоинты отображаются, но URL используют внутренние/ downstream‑пути вместо upstream‑путей.
  4. Проверены версии пакетов: Microsoft.AspNetCore.OpenApi 9.0.11, MMLib.SwaggerForOcelot 9.0.0, Swashbuckle.AspNetCore 9.0.6.

Downstream Swagger Spec

json
{
  "openapi": "3.0.4",
  "info": {
    "title": "Test API",
    "version": "1.0"
  },
  "paths": {
    "/api/weather-forecast": {
      "get": {
        "tags": ["WeatherForecast"],
        "operationId": "GetWeatherForecast",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

Upstream Swagger Spec (сгенерированный)

json
{
  "openapi": "3.0.4",
  "info": {
    "title": "Test API",
    "version": "1.0"
  },
  "paths": {},
  "components": {
    "schemas": {}
  }
}

Вопрос

Какая конфигурация или настройки мне недостаёт, чтобы корректно отображать операции swagger в объединённой документации Ocelot?

Ошибка «No operations defined in spec!» в MMLib.SwaggerForOcelot обычно возникает, когда библиотека не удаётся корректно преобразовать спецификации Swagger downstream‑сервисов в объединённый Swagger Ocelot из‑за несоответствий в конфигурации или проблем с обработкой шаблонов путей. На основе вашего окружения проблема, скорее всего, связана с тем, как библиотека обрабатывает переменные шаблонов пути во время трансформации.

Содержание

Понимание причины

Основная проблема в том, что MMLib.SwaggerForOcelot удаляет все пути из swagger.json downstream‑сервиса во время процесса трансформации Ocelot. Это происходит, потому что библиотека пытается сопоставить пути downstream‑сервиса с конфигурацией маршрутов Ocelot, но не умеет корректно обрабатывать переменные шаблонов.

Согласно GitHub issue #19, «это происходит из‑за двойных {} переменных в PathTemplate». В вашей конфигурации DownstreamPathTemplate использует /api/{url}, тогда как фактический путь downstream‑сервиса — /api/weather-forecast. Библиотека не может корректно сопоставить эти шаблоны.

Другой ключевой момент, подтверждённый исследованием на Stack Overflow, — «MMLib.SwaggerForOcelot не учитывает {version} при трансформации Ocelot», что указывает на более широкие проблемы с обработкой переменных шаблонов.

Исправления конфигурации

1. Настройка сопоставления шаблонов пути

Самое распространённое решение — убедиться, что шаблоны путей совпадают точно между маршрутом Ocelot и путями Swagger downstream‑сервиса:

json
{
  "Routes": [
    {
      "UpstreamPathTemplate": "/api/test/{url}",
      "DownstreamPathTemplate": "/api/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "test-host",
          "Port": 8080
        }
      ],
      "SwaggerKey": "test",
      "UpstreamHttpMethod": ["GET", "POST", "PUT", "DELETE"]
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "test",
      "Config": [
        {
          "Name": "Test API",
          "Version": "v1",
          "Url": "http://test-host:8080/swagger/v1/swagger.json"
        }
      ]
    }
  ]
}

2. Настройка свойства TransformByOcelotConfig

Добавьте свойство TransformByOcelotConfig в конфигурацию SwaggerEndpoints. Как отмечено в обсуждениях Stack Overflow, вы можете установить его в false, чтобы обойти трансформацию:

json
{
  "SwaggerEndPoints": [
    {
      "Key": "test",
      "TransformByOcelotConfig": false,
      "Config": [
        {
          "Name": "Test API",
          "Version": "v1",
          "Url": "http://test-host:8080/swagger/v1/swagger.json"
        }
      ]
    }
  ]
}

Однако, как вы отметили, это покажет эндпоинты с внутренними/ downstream‑путями вместо upstream‑путей. Для правильного решения оставьте true, но убедитесь, что шаблоны путей совпадают корректно.

3. Исправление конфигурации маршрута

Убедитесь, что ваша конфигурация маршрута полная и включает все необходимые свойства. Согласно GitHub issue #90, правильная конфигурация должна выглядеть так:

json
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "test-host",
          "Port": 8080
        }
      ],
      "UpstreamPathTemplate": "/api/test/{everything}",
      "UpstreamHttpMethod": ["GET", "POST", "PUT", "DELETE"],
      "SwaggerKey": "test"
    }
  ],
  "SwaggerEndPoints": [
    {
      "Key": "test",
      "Config": [
        {
          "Name": "Test API",
          "Version": "v1",
          "Url": "http://test-host:8080/swagger/v1/swagger.json"
        }
      ]
    }
  ]
}

Продвинутая отладка

1. Проверка совместимости версий пакетов

Версии ваших пакетов выглядят совместимыми, но несовпадения версий могут вызывать проблемы. Согласно NuGet Gallery, убедитесь, что вы используете совместимые версии:

  • Microsoft.AspNetCore.OpenApi 9.0.11
  • MMLib.SwaggerForOcelot 9.0.0
  • Swashbuckle.AspNetCore 9.0.6

Попробуйте протестировать с разными версиями, чтобы исключить проблемы совместимости.

2. Проверка переменных шаблонов пути

Проблема часто возникает из‑за того, как обрабатываются переменные шаблонов. Убедитесь, что имена переменных согласованы:

json
{
  "Routes": [
    {
      "UpstreamPathTemplate": "/api/test/{everything}",
      "DownstreamPathTemplate": "/api/{everything}"
      // ...
    }
  ]
}

Использование {everything} вместо {url} может помочь решить проблемы с сопоставлением шаблонов, как отмечено в нескольких обсуждениях GitHub.

3. Отладка процесса трансформации

Добавьте логирование, чтобы понять, что происходит во время трансформации. Вы можете проверить, какая спецификация Swagger загружается до и после трансформации:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

Это поможет увидеть, загружается ли downstream‑swagger корректно, но затем удаляется во время трансформации.

Альтернативные решения

1. Использование прямого шаблона пути downstream

Если вы обслуживаете только один API, рассмотрите возможность использования более прямого шаблона пути:

json
{
  "Routes": [
    {
      "UpstreamPathTemplate": "/api/test/weather-forecast",
      "DownstreamPathTemplate": "/api/weather-forecast"
      // ...
    }
  ]
}

Это устраняет проблемы с сопоставлением переменных шаблонов.

2. Реализация пользовательской трансформации Swagger

Для сложных сценариев может потребоваться реализовать пользовательскую логику трансформации. Согласно обсуждениям GitHub, вы можете программно настроить Swagger‑эндпоинты:

csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddSwaggerForOcelot(Configuration);
    services.AddOcelot();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseOcelot().Wait();
    app.UseSwaggerForOcelotUI(opt =>
    {
        opt.PathToSwaggerGenerator = "/swagger/docs";
        opt.DownstreamSwaggerEndPointBasePath = "/api/v1/swagger/docs";
    });
}

3. Использование нескольких Swagger‑ключей

Если у вас несколько сервисов, используйте отдельные Swagger‑ключи для каждого сервиса, как показано в примерной конфигурации:

json
{
  "SwaggerEndPoints": [
    {
      "Key": "bffweb",
      "TransformByOcelotConfig": false,
      "Config": [
        {
          "Name": "BFF.Web",
          "Version": "1.0",
          "Url": "http://localhost:5205/swagger/v1/swagger.json"
        }
      ]
    },
    {
      "Key": "location",
      "TransformByOcelotConfig": true,
      "Config": [
        {
          "Name": "Location.API",
          "Version": "1.0",
          "Url": "http://localhost:5205/location/swagger/v1/swagger.json"
        }
      ]
    }
  ]
}

Лучшие практики

  1. Проверяйте шаблоны путей отдельно: перед настройкой Swagger убедитесь, что маршруты Ocelot работают корректно, делая прямые вызовы API.
  2. Используйте версионированные API: включайте информацию о версии в конфигурацию Swagger, чтобы избежать конфликтов.
  3. Проверяйте downstream‑Swagger: убедитесь, что Swagger downstream‑сервиса доступен и содержит валидные операции перед настройкой Ocelot.
  4. Следите за обновлениями пакетов: держите MMLib.SwaggerForOcelot обновлённым, чтобы получать исправления ошибок и улучшения.
  5. Соблюдайте единообразие имен: держите ключи Swagger, имена маршрутов и сервисов согласованными во всей конфигурации.
  6. Тестируйте в изоляции: проверяйте каждый Swagger‑эндпоинт отдельно, прежде чем объединять несколько сервисов.

Источники

  1. Stack Overflow – No operations defined in spec when using MMLib.SwaggerForOcelot to build combined swagger for Ocelot
  2. Stack Overflow – Ocelot Swagger MMLib.SwaggerForOcelot showing “No operations defined in spec!”
  3. GitHub – MMLib.SwaggerForOcelot Issue #19: All paths and definitions are removed from swagger.json
  4. GitHub – MMLib.SwaggerForOcelot Issue #90: SwaggerEndPoints configuration section is missing or empty
  5. Think Simple – Configure Swagger on api gateway using ocelot in asp.net core application
  6. NuGet Gallery – MMLib.SwaggerForOcelot 9.0.0
  7. GitHub – MMLib.SwaggerForOcelot Discussions #153: Programmatic attempt to add Swagger Endpoints

Вывод

Ошибка «No operations defined in spec!» в MMLib.SwaggerForOcelot обычно вызвана проблемами сопоставления шаблонов путей во время процесса трансформации Swagger. Чтобы решить эту проблему:

  1. Исправьте переменные шаблонов пути, убедившись, что имена совпадают между upstream‑ и downstream‑шаблонами.
  2. Настройте TransformByOcelotConfig в соответствии с вашими потребностями маршрутизации.
  3. Проверьте полную конфигурацию маршрутов с необходимыми свойствами.
  4. Проверьте совместимость версий пакетов и при необходимости откатитесь к более стабильным версиям.
  5. Используйте прямые шаблоны пути вместо переменных, если это возможно.

Решив эти конфигурационные вопросы, вы сможете корректно отображать операции Swagger в объединённой документации Ocelot, сохраняя правильную маршрутизацию upstream‑путей. Если проблема сохраняется, рассмотрите возможность реализации пользовательской логики трансформации или использования отдельных Swagger‑ключей для разных сервисов.

Авторы
Проверено модерацией
Модерация