Back to "LLMapi: Dynamic Mock Gener: Завантажити будь- який спектр, Mock Any API"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI AI-Article API ASP.NET Core LLM LLMApi mockllmapi Nuget SignalR

LLMapi: Dynamic Mock Gener: Завантажити будь- який спектр, Mock Any API

Wednesday, 05 November 2025

ЗАУВАЖЕННЯ: Цю статтю створено як документацію для випуску у моєму пакунку nuget.

Це досить цікаво, тому я поставив його тут, але якщо це проблема для вас, будь ласка, проігноруйте її.

Вступ

Тебе когда-нибудь нужно было проверить API, что ещё не готово?

Чи хотіли розвиватися в автономному режимі без обмежень швидкості? Динамічний генератор OpenAPI надасть вам змогу завантажити будь- яку специфікацію OpenAPI і негайно створити повнофункціональний application API з реалістичними, LM-створеними даними.

Немає файлів налаштувань.Не створено кінцевих пунктів вручну.Просто включи это в подробности OpenAPI и начинай делать просьбы.

NuGet

NuGet

Ви можете знайти

graph TD
    A[Your App] --> B{External API}
    B -->|Not Ready| C[Development Blocked]
    B -->|Rate Limited| D[Can't Test Freely]
    B -->|Requires Auth| E[Complex Setup]
    B -->|Expensive| F[Cost Concerns]
    B -->|Unreliable| G[Flaky Tests]

GitHub тут

  • для проекту, для всіх публічних доменів і т. д....
  • Менеджер OpenApi
  • Проблема: розвиток блоків API

Сучасні програми залежать від десятків зовнішніх програм API.

Під час розвитку ти стикаєшся з кількома труднощами.

  1. Традиційні розв'язання пов'язані з:
  2. Висміювання вручну (застаріє, стає застарілим)
  3. Запис/ відновлення навантаження HTTP (складно підтримувати)
  4. Використання жорстко закодованих фіксацій (нереалістичних, не покриває випадків ребер)
sequenceDiagram
    participant Dev as Developer
    participant System as Mock System
    participant Spec as OpenAPI Spec
    participant LLM as Local LLM

    Dev->>System: Load spec from URL/file
    System->>Spec: Parse OpenAPI document
    Spec-->>System: Endpoints, schemas, descriptions
    System->>System: Register dynamic routes

    Dev->>System: GET /petstore/pet/123
    System->>LLM: Generate data for "Pet" schema
    LLM-->>System: Realistic pet data
    System-->>Dev: {"id": 123, "name": "Max", ...}

Вирішення: динамічне планування OpenAPI

Вкажіть систему у будь- якій специфікації OpenAPI, і автоматично:

Аналізує специфікацію

graph TB
    A[HTTP Request] --> B{Route Matches?}
    B -->|No| C[404 Not Found]
    B -->|Yes| D[DynamicOpenApiManager]
    D --> E[Find Matching Endpoint]
    E --> F[OpenApiRequestHandler]
    F --> G[Extract Schema from Spec]
    G --> H[Build LLM Prompt]
    H --> I[PromptBuilder]
    I --> J[Include Context?]
    J -->|Yes| K[OpenApiContextManager]
    J -->|No| L[LLM Client]
    K --> L
    L --> M[Get Response]
    M --> N[JsonExtractor]
    N --> O[Return Mock Data]

Відкриває всі кінцеві точки

  1. Створює реалістичні імітаційні дані за допомогою LLMОбслуговує фіктивний API на вашому локальному комп' ютері
  2. Як це працюєОгляд архітектури
  3. **Система OpenAPI складається з декількох координованих компонентів:**Компоненти ключів:
  4. DynamicOpenApiManager- Керування завантаженими специфікаціями і маршрутом, що збігаються
  5. OpenApiSpecLoader- Документи OpenAPI отримання і обробки

OpenApiRequestHandler

  • Створює відповіді на відповідні кінцеві точки

Запитувати розробника

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore"
}

- Створює підказки LLM за схемами OpenAPI

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "my-api",
  "source": "./specs/my-api.yaml",
  "basePath": "/api/v1"
}

OpenApiContextManager

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "inline-api",
  "source": "data:application/json;base64,eyJvcGVuYXBpIjoiMy...",
  "basePath": "/api"
}

- Підтримує послідовність між викликами (необов' язково)

Завантаження специфікацій

public async Task<SpecLoadResult> LoadSpecAsync(
    string name,
    string source,
    string? basePath = null,
    string? contextName = null)
{
    // 1. Use scoped service factory for OpenApiSpecLoader
    using var scope = _scopeFactory.CreateScope();
    var specLoader = scope.ServiceProvider
        .GetRequiredService<OpenApiSpecLoader>();

    // 2. Load the OpenAPI document
    var document = await specLoader.LoadSpecAsync(source);

    // 3. Determine base path from spec or parameter
    var effectiveBasePath = basePath
        ?? document.Servers?.FirstOrDefault()?.Url
        ?? "/api";

    // 4. Store spec with configuration
    var config = new OpenApiSpecConfig
    {
        Name = name,
        Source = source,
        Document = document,
        BasePath = effectiveBasePath,
        ContextName = contextName,
        LoadedAt = DateTimeOffset.UtcNow
    };

    _specs.AddOrUpdate(name, config, (_, __) => config);

    // 5. Notify listeners via SignalR
    await NotifySpecLoaded(name, effectiveBasePath);

    return new SpecLoadResult
    {
        Name = name,
        BasePath = effectiveBasePath,
        EndpointCount = CountEndpoints(document),
        Success = true
    };
}

Спекти можна завантажувати з трьох джерел:

public OpenApiEndpointMatch? FindMatchingEndpoint(string path, string method)
{
    // Try each loaded spec
    foreach (var spec in _specs.Values)
    {
        // Remove base path prefix
        var relativePath = path;
        if (path.StartsWith(spec.BasePath))
        {
            relativePath = path.Substring(spec.BasePath.Length);
        }

        // Find matching path in OpenAPI document
        var (pathTemplate, operation) = FindOperation(
            spec.Document,
            relativePath,
            method);

        if (operation != null)
        {
            return new OpenApiEndpointMatch
            {
                Spec = spec,
                PathTemplate = pathTemplate,
                Operation = operation,
                Method = ParseMethod(method)
            };
        }
    }

    return null;
}

Віддалена адреса URL

public async Task<string> HandleRequestAsync(
    HttpContext context,
    OpenApiDocument document,
    string path,
    OperationType method,
    OpenApiOperation operation,
    string? contextName = null,
    CancellationToken cancellationToken = default)
{
    // 1. Extract request body
    var requestBody = await ReadRequestBodyAsync(context.Request);

    // 2. Get success response schema
    var shape = ExtractResponseSchema(operation);

    // 3. Get context history if using contexts
    var contextHistory = !string.IsNullOrWhiteSpace(contextName)
        ? _contextManager.GetContextForPrompt(contextName)
        : null;

    // 4. Build prompt from OpenAPI metadata
    var description = operation.Summary ?? operation.Description;
    var prompt = _promptBuilder.BuildPrompt(
        method.ToString(),
        path,
        requestBody,
        new ShapeInfo { Shape = shape },
        streaming: false,
        description: description,
        contextHistory: contextHistory);

    // 5. Get response from LLM
    var rawResponse = await _llmClient.GetCompletionAsync(
        prompt,
        cancellationToken);

    // 6. Extract clean JSON
    var jsonResponse = JsonExtractor.ExtractJson(rawResponse);

    // 7. Store in context if configured
    if (!string.IsNullOrWhiteSpace(contextName))
    {
        _contextManager.AddToContext(
            contextName,
            method.ToString(),
            path,
            requestBody,
            jsonResponse);
    }

    return jsonResponse;
}

Локальний файл

private string? ExtractResponseSchema(OpenApiOperation operation)
{
    // Look for successful response (2xx)
    var successResponse = operation.Responses
        .FirstOrDefault(r => r.Key.StartsWith("2"))
        .Value;

    if (successResponse == null)
        return null;

    // Get JSON content
    var jsonContent = successResponse.Content
        .FirstOrDefault(c => c.Key.Contains("json"))
        .Value;

    if (jsonContent?.Schema == null)
        return null;

    // Convert OpenAPI schema to JSON Schema
    return ConvertToJsonSchema(jsonContent.Schema);
}

private string ConvertToJsonSchema(OpenApiSchema schema)
{
    // Recursively build JSON Schema representation
    var builder = new StringBuilder();
    builder.Append("{");

    if (schema.Type != null)
    {
        builder.Append($"\"type\":\"{schema.Type}\"");
    }

    if (schema.Properties?.Count > 0)
    {
        builder.Append(",\"properties\":{");
        var props = schema.Properties
            .Select(p => $"\"{p.Key}\":{ConvertToJsonSchema(p.Value)}");
        builder.Append(string.Join(",", props));
        builder.Append("}");
    }

    if (schema.Items != null)
    {
        builder.Append(",\"items\":");
        builder.Append(ConvertToJsonSchema(schema.Items));
    }

    builder.Append("}");
    return builder.ToString();
}

URL даних (Base64 закодовано)

Specialloading Process

Ось що відбувається, коли ви завантажуєте специфікацію:

Порівнювання динамічного маршруту

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore"
}

Після отримання запиту система відповідає їй за всі завантажені специфікації:

{
  "name": "petstore",
  "basePath": "/petstore",
  "endpointCount": 19,
  "endpoints": [
    {"path": "/petstore/pet", "method": "POST"},
    {"path": "/petstore/pet/{petId}", "method": "GET"},
    {"path": "/petstore/pet/findByStatus", "method": "GET"},
    ...
  ],
  "success": true
}

Обробка запитів

Після виявлення відповідної кінцевої точки обробник створює відповідь:

### Get a pet by ID
GET /petstore/pet/123

### Response (auto-generated):
{
  "id": 123,
  "name": "Max",
  "category": {
    "id": 1,
    "name": "Dogs"
  },
  "photoUrls": [
    "https://example.com/max1.jpg"
  ],
  "tags": [
    {"id": 1, "name": "friendly"},
    {"id": 2, "name": "trained"}
  ],
  "status": "available"
}
### Find pets by status
GET /petstore/pet/findByStatus?status=available

### Response (auto-generated array):
[
  {
    "id": 42,
    "name": "Buddy",
    "status": "available",
    ...
  },
  {
    "id": 43,
    "name": "Luna",
    "status": "available",
    ...
  }
]

Видобування схеми

GET /api/openapi/specs/petstore

### Shows full details:
### - All endpoints
### - Load time
### - Context configuration
### - Base path

Система видобуває схеми відповідей з визначень OpenAPI:

POST /api/openapi/specs/petstore/reload

Використання у реальному світі

DELETE /api/openapi/specs/petstore

Приклад: APTorre API

Давайте пройдемо через глум над класичним API Petstore:

### Load Petstore at /petstore
POST /api/openapi/specs
{"name": "petstore", "source": "...", "basePath": "/petstore"}

### Load GitHub API at /github
POST /api/openapi/specs
{"name": "github", "source": "...", "basePath": "/github"}

### Load Stripe API at /stripe
POST /api/openapi/specs
{"name": "stripe", "source": "...", "basePath": "/stripe"}

### All three APIs now available simultaneously:
GET /petstore/pet/123
GET /github/users/octocat
GET /stripe/customers/cus_123

Крок 1. Завантажити спектр

Відповідь:

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore",
  "contextName": "petstore-session"
}

Крок 2: Використовувати кінцеві точки

### Create a pet
POST /petstore/pet
{"name": "Max", "status": "available"}

### Response: {"id": 42, "name": "Max", "status": "available"}

### Get the pet (will reference same ID and name)
GET /petstore/pet/42

### Response: {"id": 42, "name": "Max", "status": "available"}
### Notice: Consistent ID and name from context

Тепер усі 19 кінцевих пунктів доступні:

Крок 3. Пильнуй за ходом подій

POST /api/openapi/test
Content-Type: application/json

{
  "specName": "petstore",
  "path": "/pet/123",
  "method": "GET"
}

### Returns mock response without affecting routes

Крок 4: Перезавантажити, якщо зміни у спектрі

  • Крок 5: Вилучити після завершення
  • Одночасно можна спостерігати різні сцени
  • Ви можете одночасно завантажити декілька специфікацій, кожна з яких має свій базовий шлях:

Спекти з контекстами

Для ще більш реалізму, призначте контекст умовам:http://localhost:5116/OpenApi:

graph TD
    A[OpenAPI Manager UI] --> B[Load Spec Section]
    A --> C[Spec List]
    A --> D[Context Viewer]

    B --> E[URL Input]
    B --> F[JSON Input]
    B --> G[Context Configuration]

    C --> H[Spec Card]
    H --> I[Reload Button]
    H --> J[Delete Button]
    H --> K[View Endpoints]

    D --> L[Active Contexts]
    L --> M[Context Details]
    L --> N[Clear Context]

Тепер всі точки перетину тварин мають однаковий контекст:

  • Перевірка кінцевої точкиЗа допомогою кінцевої точки тесту ви можете спробувати кінцеву точку без реального запиту:
  • **Це корисно для:**Перегляд відповідей перед інтеграцією
  • Перевірка певних кінцевих точок у ізольованніПроблеми з схемою зневаджування
  • Інтерфейс керуванняДля візуального керування, відвідати
  • **Можливості:**Перетягування зі скиданням
  • вивантаження файла specialВідкриття кінцевої точки життя

- Дивитися всі кінцеві точки миттєво

Перевірка одного клацання

  • Перевірити будь-яку кінцеву точку за допомогою кнопки
# OpenAPI Spec
/pet/{petId}:
  get:
    parameters:
      - name: petId
        in: path
        schema:
          type: integer
GET /petstore/pet/123
### LLM receives: "Generate data for Pet with petId=123"
### Response: {"id": 123, ...}

Сповіщення у режимі реального часу

  • Поновлення сигналів під час завантаження специфікацій
/pet/findByStatus:
  get:
    parameters:
      - name: status
        in: query
        schema:
          type: string
          enum: [available, pending, sold]
GET /petstore/pet/findByStatus?status=available
### LLM receives: "Generate array of Pets with status=available"
### Response: [{"status": "available", ...}, ...]

Підсвічування синтаксису

  • Прекрасне відображення відгуку JSON
/pet:
  post:
    requestBody:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Pet'
POST /petstore/pet
Content-Type: application/json

{"name": "Max", "status": "available"}

### LLM receives: "Generate response for creating Pet with name=Max, status=available"
### Response: {"id": 42, "name": "Max", "status": "available"}

Керування контекстом

  • Перегляд і ясний контекст
/pet/{petId}:
  get:
    summary: Find pet by ID
    description: Returns a single pet based on the ID provided

Додаткові можливості

Параметри шляхів

Параметри шляху буде автоматично видобуто:

responses:
  '200':
    description: Successful operation
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/Pet'
  '404':
    description: Pet not found

Параметри запиту

Параметри запиту впливають на відповідь:

Потрібні тіла

public async Task NotifySpecLoaded(string name, string basePath)
{
    await _hubContext.Clients.All.SendAsync("SpecLoaded", new
    {
        name,
        basePath,
        timestamp = DateTimeOffset.UtcNow
    });
}

public async Task NotifySpecDeleted(string name)
{
    await _hubContext.Clients.All.SendAsync("SpecDeleted", new
    {
        name,
        timestamp = DateTimeOffset.UtcNow
    });
}

Тіла POST/PUT включено до запрошення:

const connection = new signalR.HubConnectionBuilder()
    .withUrl('/hubs/openapi')
    .build();

connection.on('SpecLoaded', (data) => {
    showNotification(`Spec "${data.name}" loaded at ${data.basePath}`, 'success');
    refreshSpecList();
});

connection.on('SpecDeleted', (data) => {
    showNotification(`Spec "${data.name}" deleted`, 'info');
    refreshSpecList();
});

Описи і списки

Описи OpenAPI стосуються LLM:

  • Ці є включені в запрошення, допомагаючи LLM зрозуміти кінцеву ціль.
  • Коди стану відповіді
  • Система використовує першу успішну (2xx) відповідь:
  • На даний момент симуляція використовується лише 200 схем для створення висміювання (404- ті).
  • Оновлення реального часу сигналів

Якщо специфікації завантажено/ вилучено, програма UI отримує сповіщення у режимі реального часу за допомогою SignR:

Код інтерфейсу JavaScript:

Підтримка форматів

 Bad:  {"name": "spec1", ...}
 Good: {"name": "github-v3", ...}

Система підтримує:

OpenAPI 3. 0. x

### Good separation
/petstore/...
/github/...
/stripe/...

### Bad (conflicts!)
/api/... (multiple specs)

OpenAPI 3. 1. x

Swagger 2. 0

POST /api/openapi/specs/my-api/reload

Формат JSON

POST /api/openapi/specs
{
  "name": "petstore",
  "source": "...",
  "contextName": "test-session"
}

### Now all petstore calls maintain consistency

Формат YML

І специфікації JSON, і YAML автоматично розпізнаються і аналізуються.

DELETE /api/openapi/specs/old-spec

Найкращі вправи

  1. **1.**Використовувати описовані назви спектрів
  2. **2.**Встановити відповідні базові шляхи
  3. **Уникайте конфліктів за допомогою унікальних базових шляхів:**3.
  4. Перезавантажити під час зміни спектрівЯкщо ваш параметр OpenAPI буде оновлено, перезавантажте його:
  5. **4.**Використовувати контексти для пов' язаних викликів

5.

Очищення після випробування/api/mockВилучити специфікації, які ви більше не використовуєте:

### OpenAPI-based (from spec)
GET /petstore/pet/123
### Uses Pet schema from OpenAPI spec

### Regular mock (shape-based)
GET /api/mock/custom?shape={"id":0,"name":"string"}
### Uses explicit shape parameter

Обмеження

Коди стану відповіді

- Тільки успішні (2xx) відповіді висміюють

Розпізнавання

private readonly ConcurrentDictionary<string, OpenApiSpecConfig> _specs = new();

- Заголовки автентифікації приймаються, але не підтверджуються

DynamicOpenApiManagerПеревірка

- Запит на перевірку схеми не є примусовим

Стан

### Send these simultaneously
POST /api/openapi/specs {"name": "spec1", ...}
POST /api/openapi/specs {"name": "spec2", ...}
POST /api/openapi/specs {"name": "spec3", ...}
  • Немає справжньої бази даних; дані створюються кожного разу (якщо ви не використовуєте контекст)

Швидкодія

- Покоління LLM додає скасування (~100- 500 мс на запит)

Інтеграція з регулярними точками Mock EndsСпецифічні специфікації OpenAPI працюють разом з звичайними

кінцеві точки:

  • Обидва використовуються однаковим базовим LLM, але відрізняються у спосіб, у який встановлюється схема.
  • Оптимізація швидкодії
  • Кечінгgreat- britain_ counties. kgm
  • Завантажені специфікації зберігаються у пам' яті:

Служіння

**Это одинак, так что предположения остаются заряжены на всю программу.**Завантаження паралельного спектру

Декілька специфікацій можна завантажити паралельно:

  • Всі три будуть завантажуватися паралельно, а не по суті./petstore + /pet/123 = /petstore/pet/123
  • Вирішення проблем
  • Спектакль не завантажується

Проблема:

Помилка завантаження specialВирішення:

Перевірте, чи доступна адреса URL

  • Перевірити існування файла (для локальних шляхів)
  • Переконатися, що JSON/YML коректний
  • Шукайте проблем CORS (для віддалених адрес URL)

Кінцеву точку не знайдено

Проблема:

404 на очікуваній кінцевій точці

  • **Вирішення:**Перевірити базовий шлях:
  • Перевірити специфікацію насправді визначає цей шляхЗабезпечити відповідність методів HTTP (GUT проти POST)
  • Відповідь не відповідає схеміПроблема:
  • Створені дані не відповідають очікуваній схеміВирішення:
  • Позначте, якщо схема у специфікації правильнаПеревірка правильної відповіді (200 проти 201)

Пам' ятайте: покоління LLM є пробабілістичним, а не детерміністським

Висновки

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.