Back to "LLMApi: OpenAPI 动态模拟生成器@:装载任何 SpecMS K2"

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 动态模拟生成器@:装载任何 SpecMS K2

Wednesday, 05 November 2025

这篇文章主要是作为我发行文档 .QI It'Q的金字塔软件包的一部分产生的人工智能, 我把它放在这里很有意思 所以如果这是个问题 请忽略它 @.Q

一. 导言导言 导言 导言 导言

您是否需要测试一个 API 尚未准备好的 '}( ?}) 或者想要在不设速率限制的情况下开发离线性能...?。 OpenAPI动态模拟生成器允许您装入任何OpenAPI规格, 并立即用符合实际的 <,> LLM=-生成数据*.}创建完整的-#功能模拟API?

没有配置文件@.没有手动创建端点 @ . 只要将它指向 OpenAPI spec 并开始请求\ MS K2

Nuget 元数 Nuget 元数

你可以找到 吉特Hub在这里 所有公共域等 ... *

打开Api 管理器

问题=:}AIP 依赖性区块开发

现代应用程序取决于几十个外部APIs.}在开发期间 <%,}你面临数项挑战<_:}*

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]

传统解决办法涉及:

  • 手写模拟回应@(tidious,}成为过时的 @)
  • 正在记录“/”重放 HTTP 流量“ @ (_ brittle@ MS K2} 难以维护的 hard to keep k3
  • 使用硬码固定装置 @ (@unacticatetic=,} does'_ t覆盖边缘情况@)+%

解答@:动态的 OpenAPI 模拟

将系统指向任何 OpenAPI spec,,并自动标记为:

  1. 解析规格
  2. 发现所有终点
  3. 使用 LLM 生成现实的模拟数据
  4. 将模拟APIP服务在本地机器上
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系统由几个协调组件组成 :

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. 动态开放管理器 - 管理已装入的参数和路线匹配
  2. Open spiSpeecloader 打开光线 - 获取并解析 OpenAPI 文件
  3. OpenApi 请求挂载器 - 生成匹配端点的响应
  4. 即时建筑 - 创建 OpenAPI 方案下的 LLM 提示
  5. OpenApicon Text 管理器 @- 保持调用一致@( optional)

装入指定

可以从三个来源装入光谱@: @%

1. 远程 URL

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

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

2. 本地文件

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

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

3. 数据 URL {(}_Base\64 编码=)

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

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

标注加载进程

这里“ '} ” 当你装入一个 ppec : 时会发生什么?

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

动态线路匹配

当一个请求到达时, 系统与所有已装入的speces:匹配

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

请求处理

一旦找到匹配的端点 @ , 处理器生成响应Q:\

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

Schema 采掘

系统从 OpenAPI 定义中提取响应方案@:

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

世界使用量

例如::

Let''s 走通过 嘲弄经典的宠物商店API:

*步骤 \ 1: 装入闪光

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
}

使用模拟末点

现在所有19的终点都可用\ :

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

步骤“3:”检查光谱

GET /api/openapi/specs/petstore

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

*步骤 4: {如果插件改变则重新装入

POST /api/openapi/specs/petstore/reload

步骤 @5: 移除完成后删除

DELETE /api/openapi/specs/petstore

多重分层同时

您可以同时加载多个参数@ , @ 每个人的基路径\ :} @ action: check

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

带有上下文的特征

为了更多现实性 , 给一个spec:\ 指定上下文

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

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

现在所有宠物店端点都共享相同的上下文@: @%

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

测试终点

测试端点允许您尝试端点而不提出真正的请求@ : @

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

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

### Returns mock response without affecting routes

此功能对 @ : @ action

  • 整合前预览响应
  • 单独测试特定终点
  • 调试计划问题

管理统一UI

视觉管理访问@,_ 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]

功能@: @%

  • 拖放@ - @ 和 @ MS K1 @ put spec 文件上传
  • 活端点发现 @- 立即看到所有终点
  • One - 点击测试 - 用按钮测试任何端点
  • Real-}时间通知 - 信号R 当标本装入时更新@ info: status
  • 语法突出 美丽的JSON应答显示
  • 背景管理 - 视图和清晰的背景

高级特征

路径图参数

路径参数被自动提取@: @%

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

请求机构

POST/PUT 机构包含在提示@:中

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

说明和摘要

OpenAPI 描述指导 LLM: @%

/pet/{petId}:
  get:
    summary: Find pet by ID
    description: Returns a single pet based on the ID provided

这些都包含在快报“,”中 帮助LLM了解终点 “'”目的“MS K2”

反应状态法

系统使用第一个成功的 @(2_xx_)}回应@:

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

只有 200 schema 用于模拟代号@(404目前尚未模拟 @).}

信号R Real=-}时间更新

当装入 specs\ @ / deleted\ MS K1 时, UI 通过 SignalR: 接收 realQ-\ 时间通知 *

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

JavaScript 用户界面代码@: @%

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 3.0.x
  • OpenAPI 3.1.x
  • 斯瓦格 2.0
  • JSON 格式
  • YAML 格式

JSON和YAML的特征都自动检测并分析

最佳做法

1. 使用描述性插件名称

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

2. 设置适当的基础路径

使用独特的基准路径来避免冲突@: @%

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

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

specs change 时重新装入 3.

如果您的 OpenAPI spec 已被更新@ , @ reload it:}

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

4. 相关调用使用背景

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

### Now all petstore calls maintain consistency

@5. 测试后清理

删除您're 不再使用@ :

DELETE /api/openapi/specs/old-spec

限制

  1. 反应状态法 @- @ 仅成功 @ (2_xx#)}回应被嘲笑
  2. 验证 - Auth 信头被接受但未经验证
  3. 审定 *- 请求对计划进行验证时未执行
  4. 状态国 “- ” 没有实际数据库“; 数据每次生成新鲜数据, @( 除非使用上下文@)
  5. 业绩表现 实绩 “-LLM 一代”加上“(~100-500ms”,

与常规模拟末端点整合

OpenAPI 特征与常规平行工作 /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

两者都使用相同的底部LLM,但在如何提供方案上却各有不同。

业绩优化

缓缓

在内存中缓存已装入的参数@ :

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

使用寿命

DynamicOpenApiManager 是一个单子@ ,} 所以应用程序使用寿命的特性仍然被装入\ MS K1 @

平行镜表加载

多个特性可以平行装入@ : @

### Send these simultaneously
POST /api/openapi/specs {"name": "spec1", ...}
POST /api/openapi/specs {"name": "spec2", ...}
POST /api/openapi/specs {"name": "spec3", ...}

全部三个将同时装入 , 不是顺序\ MPK1\

排除故障

Spec Won'% t 加载

问题M: 显示装入失败

解决方案#:

  • 检查 URL 可访问
  • 校验已存在的本地路径@) @% 1
  • 确保 JSON/YAML 有效
  • 寻找远程 URLs=% )

未找到结束点

问题M: 404在预期端点

解决方案#:

  • 校验基路 #:# /petstore + /pet/123 = /petstore/pet/123
  • 检查 pec 实际定义此路径
  • 确保 HTTP 方法与 @ (+GET 相对于 POPST@)+%

回应('t匹配 Schema)

问题M: 生成的数据与预期的 schema 匹配@ ' @ t

解决方案#:

  • 检查规格中的图案是否正确
  • 校验您是否看到正确回应@(200 @ vs @201)}
  • 记住:LLM一代是概率=,不是决定性的

结论结论

OpenAPI 动态模拟鼠标生成器将任何OpenAPI规格转换成一个完全的“-”功能模拟API, 在秒@. 没有手动配置,没有硬码响应 @,没有维护管理@MS K4

LLM产生符合您的 schemas ,的不同数据,

  • 前端开发 后端正在建设中
  • 集成测试 不打实际 API
  • 离线发展 连接有限时
  • 装入测试 无配额问题
  • 原型设计 快速新特性

加上API的背景语境@,,你得到的模拟API能够保持所有电话之间的一致性\ ,提供更现实的生产系统模拟=.

模拟API的未来是充满活力的#,智能_,,

logo

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