Back to "llmapi: OpenAPI Dynamic Mock Generator: Lataa mikä tahansa spektri, Mock 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: OpenAPI Dynamic Mock Generator: Lataa mikä tahansa spektri, Mock API

Wednesday, 05 November 2025

HUOMAUTUS: Tämä artikkeli on ensisijaisesti tekoälyä, joka on syntynyt osana uutuuspakettiani julkaisudokumenttina.

Se on aika mielenkiintoinen, joten laitoin sen tähän, mutta jos se on ongelma sinulle, ole hyvä ja jätä se huomiotta.

Johdanto

Oletko koskaan joutunut testaamaan API:tä, joka ei ollut vielä valmis?

Vai halusiko hän kehittyä pois verkosta ilman hintarajoitusta? OpenAPI Dynamic Mock Generator -generaattorin avulla voit ladata minkä tahansa OpenAPI-erittelyn ja luoda välittömästi täysin toimivan pilkkurajapinnan, jossa on realistista, LLM:n tuottamaa dataa.

Ei asetustiedostoja.Ei manuaalista päätetapahtuman luontia.Osoita sillä OpenAPI-spekti ja ala tehdä pyyntöjä.

NuGet

NuGet

Voit löytää

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 tässä.

  • projektia varten kaikki julkisuus ym....
  • OpenApi-hallinta
  • Ongelma: API-riippuvuus estää kehityksen

Modernit sovellukset ovat riippuvaisia kymmenistä ulkoisista sovellusliittymistä.

Kehitystyön aikana kohtaat useita haasteita:

  1. Perinteisiin ratkaisuihin kuuluu:
  2. Manuaalisesti kirjoitettuja valevastauksia (tekeviä, vanhentuneita)
  3. HTTP-liikenteen nauhoittaminen/toisto (hauraan, vaikeasti ylläpidettävä)
  4. Käyttämällä kovakoodattuja varusteita (epärealistisia, ei peitä reunakoteloita)
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", ...}

Ratkaisu: Dynamic OpenAPI Mocking

Osoita järjestelmä mihin tahansa OpenAPI-spektiin ja automaattisesti:

Parsauttaa eritelmän

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]

Havaitsee kaikki päätetapahtumat

  1. Luo realistista valedataa LLM:n avullaTarjoaa pilkullisen API:n paikalliselle koneellesi
  2. Miten se toimiiArkkitehtuurin yleiskatsaus
  3. **OpenAPI-järjestelmään kuuluu useita koordinoituja komponentteja:**Avainosat:
  4. DynamicOpenApiManager- Hallitsee ladattuja speksejä ja reittien täsmäyttämistä
  5. OpenApiSpecLoader- Lehdet ja parses OpenAPI-asiakirjat

OpenApiRequestHandler

  • Luo vasteita vastaaville päätetapahtumille

Promptbuilder

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

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

- Luo LLM-vihjeitä OpenAPI-skeemasta

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"
}

- Pysyy yhdenmukaisena puheluiden välillä (valinnainen)

Lastauserittelyt

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
    };
}

Näytteitä voi ladata kolmesta lähteestä:

1 Täysosuma

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;
}

Etäverkko- osoite

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;
}

Paikallinen tiedosto

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();
}

Data URL (Base64 koodattu)

Spec-latausprosessi

Näin käy, kun lataat spektaakkelin:

Dynaaminen reitti täsmää

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

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

Kun pyyntö saapuu, järjestelmä vastaa kaikkia ladattuja tietoja:

{
  "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
}

Pyyntöjen käsittely

Kun vastaava päätetapahtuma on löytynyt, käsittelijä saa aikaan vasteen:

### 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",
    ...
  }
]

Schema-uutto

GET /api/openapi/specs/petstore

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

Järjestelmä poimii vastaussteemat OpenAPI-määritelmistä:

POST /api/openapi/specs/petstore/reload

Tosielämän käyttö

DELETE /api/openapi/specs/petstore

Esimerkki: Petstoren API

Kävellään läpi ja pilkataan Petstore API:n klassikkoa:

### 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

Vaihe 1: Ladatkaa spektaakkeli

Vaste:

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

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

Vaihe 2: Käytä Mock Endpoint -pisteitä

### 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

Nyt kaikki 19 päätetapahtumaa ovat saatavilla:

Vaihe 3: Tutki spektaakkelit

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

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

### Returns mock response without affecting routes

Vaihe 4: Lataa uudelleen, jos spektaakkeli muuttuu

  • Vaihe 5: Poista kun olet valmis
  • Useita spektrogeenejä samanaikaisesti
  • Voit ladata useita speksejä kerralla, joista jokaisella on oma peruspolkunsa:

Spektaakkeleita, joissa on kontekstit

Vielä realismia ajatellen määritä tekstiyhteys spektaakkeliin: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]

Nyt kaikilla lemmikkikaupan päätetapahtumilla on sama konteksti:

  • Testin päätepisteTestin päätetapahtuman avulla voit kokeilla päätetapahtumaa tekemättä todellista pyyntöä:
  • **Tästä on hyötyä:**Vastausten esiarviointi ennen kotoutumista
  • Spesifisten päätetapahtumien testaus eristyksissäVianetsintäön liittyvät ongelmat
  • HallinnointiVisual management, vierailu
  • **Ominaisuudet:**Vedä ja pudota
  • Spec-tiedoston latausElävän päätetapahtuman löydös

- Katso kaikki päätetapahtumat välittömästi

Yhden naksahduksen testaus

  • Testaa kaikki päätetapahtumat painikkeella
# 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, ...}

Reaaliaikaiset ilmoitukset

  • SignalR-päivitykset, kun mittaukset latautuvat
/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", ...}, ...]

Syntaksin korostus

  • Kaunis JSON-vastausnäytös
/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"}

Kontekstin hallinta

  • Näkemykset ja selkeät taustat
/pet/{petId}:
  get:
    summary: Find pet by ID
    description: Returns a single pet based on the ID provided

Kehittyneet ominaisuudet

Polkumuuttujat

Polkuparametrit poistetaan automaattisesti:

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

Kyselyparametrit

Kyselyparametrit vaikuttavat vasteeseen:

Pyynnön esittäneet elimet

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-elimet ovat mukana pikaviestissä:

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();
});

Kuvaus ja yhteenvedot

OpenAPI-kuvaukset ohjaavat LLM:ää:

  • Nämä ovat mukana pikaviestissä, joka auttaa LLM:tä ymmärtämään päätetapahtuman tarkoituksen.
  • Vastaustilakoodit
  • Järjestelmässä käytetään ensimmäistä onnistunutta (2xx) vastausta:
  • Vain 200 skemaa käytetään pilkkasukupolveen (404:ää ei tällä hetkellä simuloida).
  • SignalR reaaliaikaiset päivitykset

Kun tiedot ladataan/poistetaan, UI saa reaaliaikaisia ilmoituksia SignalR:n kautta:

JavaScript UI-koodi:

Muotoilutuki

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

Järjestelmä tukee:

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-muoto

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

### Now all petstore calls maintain consistency

YAML-muoto

Sekä JSON- että YAML-spektit havaitaan ja jäsennetään automaattisesti.

DELETE /api/openapi/specs/old-spec

Parhaita käytäntöjä

  1. 1 TäysosumaKäytä kuvailevia spektinimiä
  2. **2.**Aseta sopivat peruspolut
  3. **Vältä ristiriitoja käyttämällä ainutlaatuisia peruspolkuja:**3.
  4. Lataa uudelleen, kun näytöt muuttuvatJos OpenAPI-spektisi päivittyy, lataa se uudelleen:
  5. NelonenKäytä taustatietoja lähipuheluihin

5.

Puhdista testien jälkeen/api/mockPoista tiedot, joita et enää käytä:

### 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

Rajoitukset

Vastaustilakoodit

- Vain onnistuneita (2xx) vastauksia pilkataan

Todentaminen

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

- Auth-otsikot hyväksytään, mutta niitä ei validoida

DynamicOpenApiManagerValidointi

- Pyyntöä varmennuksen saamiseksi skeemaa vastaan ei panna täytäntöön

Valtio

### Send these simultaneously
POST /api/openapi/specs {"name": "spec1", ...}
POST /api/openapi/specs {"name": "spec2", ...}
POST /api/openapi/specs {"name": "spec3", ...}
  • Ei varsinaista tietokantaa; tiedot saadaan tuoreina joka kerta (paitsi jos käytetään kontekstia)

Suorituskyky

- LLM-sukupolvi lisää latenssia (noin 100-500 ms per pyyntö)

Kotoutuminen tavallisiin mock-päätteisiinOpenAPI:n tiedot toimivat säännöllisesti

päätetapahtumat:

  • Molemmat käyttävät samaa pohjana olevaa LLM:tä, mutta eroavat siitä, miten skeema annetaan.
  • Suorituskyvyn optimointi
  • Välilyöntejä
  • Ladatut tiedot ovat muistissa:

Palvelun elinikä

**on singleton, joten speksit pysyvät ladattuina sovelluksen koko käyttöiän ajan.**Rinnakkaisnäytön lataus

Useita speksejä voidaan ladata rinnakkain:

  • Kaikki kolme lastaavat rinnakkain, eivät peräkkäin./petstore + /pet/123 = /petstore/pet/123
  • Vianetsintä
  • Spektaakkeli ei lataudu

Ongelma:

Spec-lataus epäonnistuiRatkaisut:

Tarkista, että URL on saatavilla

  • Varmista, että tiedosto on olemassa (paikallisille poluille)
  • Varmista, että JSON/YAML on voimassa
  • Etsi CORS-numeroita (etäiset URL-osoitteet)

Loppukohtaa ei löytynyt

Ongelma:

Odotettavissa oleva päätetapahtuma 404

  • **Ratkaisut:**Varmista peruspolku:
  • Tarkista spek itse määrittää, että polkuVarmista HTTP-menetelmän ottelut (GET vs. POST)
  • Reaktio ei sovi yhteen Scheman kanssaOngelma:
  • Luotu data ei vastaa odotettua skeemaaRatkaisut:
  • Tarkista, onko spektaakkelin skeema oikeinVarmista, että vastaus on oikea (200 vs. 201)

Muista: LLM-sukupolvi on probabilistinen, ei deterministinen

Päätelmät

logo

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