LLMAPI: OpenAPI Dynamic Mock Generator: Ladda alla specifikationer, Mock API
OBS!Denna artikel är främst AI genererad som en del av mitt nugetpaket som utgivningsdokumentation.
Det är ganska intressant så jag la det här men om det är ett problem för dig, var snäll och ignorera det.
Inledning
Har du någonsin behövt testa mot ett API som inte var redo än?
Inga inställningsfiler.Inget manuellt målskapande.Rikta den bara mot en OpenAPI spec och börja göra förfrågningar.

Hämta
Du kan hitta
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 här
- för projektet, alla offentliga domäner etc...
- OpenApi- hantering
- Problemet: API-beroenden Blockutveckling
Moderna applikationer är beroende av dussintals externa API:er.
Under utvecklingen står du inför flera utmaningar:
- Traditionella lösningar omfattar följande:
- Manuellt skriva hånfulla svar (tröttligt, blir föråldrad)
- Inspelning/respelning av HTTP-trafik (svår, svår att underhålla)
- Använda hårdkodade fixturer (orealistiska, täcker inte kant fall)
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", ...}
Lösningen: Dynamisk OpenAPI-mockning
Rikta systemet mot någon OpenAPI spec, och det automatiskt:
Tolkning av specifikationen
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]
Upptäcker alla endpoints
- Genererar realistiska prototypdata med hjälp av en LLMServerar prototyp API på din lokala maskin
- Hur den fungerarÖversikt över arkitekturen
- **OpenAPI-systemet består av flera samordnade komponenter:**Nyckelkomponenter:
- DynamiskOpenApiManager- Hanterar laddade specifikationer och ruttmatchning
- OpenApiSpecLoader- Fetcher och tolkar OpenAPI dokument
OpenApiRequestHandler
- Genererar svar för matchade endpoints
Snabbt bygge
POST /api/openapi/specs
Content-Type: application/json
{
"name": "petstore",
"source": "https://petstore3.swagger.io/api/v3/openapi.json",
"basePath": "/petstore"
}
- Skapar LLM- samtal från OpenAPI scheman
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"
}
- Bibehåller överensstämmelse mellan samtal (frivilligt)
Lastspecifikationer
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
};
}
Specifikationer kan laddas från tre källor:
- Vad är det som händer?
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;
}
Fjärrwebbadress
Två.
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;
}
Lokal fil
- Vad är det som händer?
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();
}
Datawebbadress (Base64 kodad)
Specific Laddar process
Så här går det när man laddar en spec:
Dynamisk ruttmatchning
POST /api/openapi/specs
Content-Type: application/json
{
"name": "petstore",
"source": "https://petstore3.swagger.io/api/v3/openapi.json",
"basePath": "/petstore"
}
När en begäran kommer, matchar systemet den mot alla laddade specifikationer:
{
"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
}
Hantering av begäran
När ett matchande resultatmått har hittats genererar hanteraren ett svar:
### 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",
...
}
]
Utdragning av schema
GET /api/openapi/specs/petstore
### Shows full details:
### - All endpoints
### - Load time
### - Context configuration
### - Base path
Systemet extraherar responsscheman från OpenAPI-definitioner:
POST /api/openapi/specs/petstore/reload
Real-World användning
DELETE /api/openapi/specs/petstore
Exempel: Petstore API
Låt oss gå igenom håna den klassiska Petstore API:
### 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
Steg 1: Ladda specifikationen
Svar:
POST /api/openapi/specs
Content-Type: application/json
{
"name": "petstore",
"source": "https://petstore3.swagger.io/api/v3/openapi.json",
"basePath": "/petstore",
"contextName": "petstore-session"
}
Steg 2: Använd Mock Endpoints
### 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
Nu finns alla 19 endpoints tillgängliga:
Steg 3: Inspektera specifikationerna
POST /api/openapi/test
Content-Type: application/json
{
"specName": "petstore",
"path": "/pet/123",
"method": "GET"
}
### Returns mock response without affecting routes
Steg 4: Ladda om om specifika ändringar
- Steg 5: Ta bort när det är klart
- Flera specifikationer samtidigt
- Du kan ladda flera specifikationer på en gång, var och en med sin egen basväg:
Specifikationer med sammanhang
För ännu mer realism, tilldela ett sammanhang till en spec: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]
Nu delar alla slutpunkter i djurbutiken samma sammanhang:
- ProvningsslutpunktTest endpoint kan du prova en endpoint utan att göra en verklig begäran:
- **Detta är användbart för:**Förhandsgranskning av svar före integration
- Testning av specifika endpoints isoleratFelsökning av schemafrågor
- LedningsgränssnittFör visuell hantering, besök
- **Kännetecken:**Dra och släpp
- Spec- filuppladdningUpptäckt av levande endpoint
- Se alla endpoints omedelbart
Enklickstestning
- Testa alla endpoints med en knapp
# 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, ...}
Anmälan i realtid
- SignalR-uppdateringar när specsen laddas
/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", ...}, ...]
Syntaxfärgläggning
- Vacker JSON svarsdisplay
/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"}
Sammanhangshantering
- Visa och tydliga sammanhang
/pet/{petId}:
get:
summary: Find pet by ID
description: Returns a single pet based on the ID provided
Avancerade funktioner
Parametrar för sökväg
Sökvägsparametrar extraheras automatiskt:
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'404':
description: Pet not found
Köningsparametrar
Frågaparametrar påverkar svaret:
Begärande organ
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 organ ingår i prompten:
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();
});
Beskrivningar och sammanfattningar
OpenAPI-beskrivningar vägleder LLM:
- Dessa ingår i prompten, vilket hjälper LLM att förstå endpointens syfte.
- Koder för svarsstatus
- Systemet använder det första lyckade (2xx) svaret:
- Endast 200 schema används för mock generation (404s för närvarande inte simuleras).
- SignalR realtidsuppdateringar
När specifikationer laddas/utgår tar UI emot realtidsmeddelanden via SignalR:
JavaScript UI- kod:
Formatstöd
Bad: {"name": "spec1", ...}
Good: {"name": "github-v3", ...}
Systemet stöder:
OpenAPI 3.0.x
### Good separation
/petstore/...
/github/...
/stripe/...
### Bad (conflicts!)
/api/... (multiple specs)
OpenAPI 3.1.x
Swagger 2.0 Ordförande
POST /api/openapi/specs/my-api/reload
JSON- format
POST /api/openapi/specs
{
"name": "petstore",
"source": "...",
"contextName": "test-session"
}
### Now all petstore calls maintain consistency
YAML- format
Både JSON och YAML specs detekteras och tolkas automatiskt.
DELETE /api/openapi/specs/old-spec
Bästa praxis
- **1. Vad är det som händer?**Använd beskrivande specifikationer
- **Två.**Ställ in lämpliga basvägar
- **Undvik konflikter genom att använda unika basvägar:**3. Vad är det som händer?
- Ladda om när specifikationer ändrasOm din OpenAPI-specifikation uppdateras, ladda om den:
- **Fyra.**Använd sammanhang för relaterade samtal
För att uppnå detta mål har kommissionen beslutat att inleda ett förfarande enligt artikel 108.2 i fördraget om Europeiska unionens funktionssätt (nedan kallat EUF-fördraget).
Städa upp efter testning/api/mockTa bort specifikationer du inte längre använder:
### 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
Begränsningar
Koder för svarsstatus
- Endast framgångsrika (2xx) svar hånas
Behörighetskontroll
private readonly ConcurrentDictionary<string, OpenApiSpecConfig> _specs = new();
- Auth-huvuden accepteras men är inte validerade
DynamicOpenApiManagerValidering
- Begära validering mot scheman inte verkställs
Tillstånd
### Send these simultaneously
POST /api/openapi/specs {"name": "spec1", ...}
POST /api/openapi/specs {"name": "spec2", ...}
POST /api/openapi/specs {"name": "spec3", ...}
- Ingen verklig databas; data genereras färska varje gång (om inte med hjälp av sammanhang)
Prestanda
- LLM generation lägger latency (~100-500ms per begäran)
Integrering med regelbundna Mock EndpointsOpenAPI specs fungerar tillsammans med vanliga
ändpunkter:
- Båda använder samma underliggande LLM men skiljer sig åt i hur schemat tillhandahålls.
- Prestandaoptimering
- Caching
- Laddade specifikationer lagras i minnet:
Tjänstlivstid
**är ett singleton, så specifikationerna förblir laddade under applikationens livstid.**Parallella specifikationer som laddar
Flera specifikationer kan laddas parallellt:
- Alla tre kommer att ladda parallellt, inte sekventiellt.
/petstore+/pet/123=/petstore/pet/123 - Felsökning
- Spec kommer inte att ladda
Problem:
Specificering misslyckasLösningar:
Kontrollera att webbadressen är tillgänglig
- Verifiera filen finns (för lokala sökvägar)
- Se till att JSON/YAML är giltig
- Leta efter CORS-frågor (för fjärrwebbadresser)
Slutpunkt hittades inte
Problem:
404 för förväntat effektmått
- **Lösningar:**Verifiera bassökvägen:
- Markera spec definierar faktiskt den sökvägenSe till att HTTP-metoden matchar (GET vs POST)
- Svar matchar inte schematProblem:
- Genererad data matchar inte förväntat schemaLösningar:
- Kontrollera om schemat i specifikationen är korrektKontrollera att du tittar på rätt svar (200 vs 201)
Kom ihåg: LLM-generationen är probabilistisk, inte deterministisk
Slutsatser