8
n8n 中文网amn8n.com

使用 n8n API 和 Bootstrap 为 Webhooks 生成自文档化 API 门户

高级

这是一个Document Extraction, Multimodal AI领域的自动化工作流,包含 17 个节点。主要使用 N8n, Set, Code, Html, Webhook 等节点。 使用 n8n API 和 Bootstrap 为 Webhooks 生成自文档化 API 门户

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "e860732ed76ff1de8212e780b2d62bd140dee0b71e6ccf7172d54e965acae43d",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "a59b516a-e188-4364-8722-f83fd00ed0f0",
      "name": "聚合",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        120,
        -300
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "3ab41431-6298-4380-aaec-d0e29f7f9cb5",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -700,
        460
      ],
      "webhookId": "e98f9bf5-f5ad-46fb-a93c-08218eea8d5e",
      "parameters": {
        "path": "api-doc",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "286e23f0-44c8-46c4-bfd2-8aab6eae245b",
      "name": "配置",
      "type": "n8n-nodes-base.set",
      "position": [
        -480,
        460
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9a243289-a359-47fd-9155-a809a910b8f7",
              "name": "name_doc",
              "type": "string",
              "value": "N8N Example Doc"
            },
            {
              "id": "59992f15-d27d-454c-9c1f-a66f0674c7bb",
              "name": "version",
              "type": "string",
              "value": "v.0.1"
            },
            {
              "id": "f0633ecb-b0c8-4e55-8285-51aa05492420",
              "name": "description",
              "type": "string",
              "value": "Example description"
            },
            {
              "id": "2da37a16-f97a-4ec8-9d9c-13f09bea1d39",
              "name": "url_base",
              "type": "string",
              "value": "n8n.io/"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b910c088-c3c6-4bfc-9856-5294891e4cd0",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -370,
        -400
      ],
      "parameters": {
        "color": 2,
        "width": 860,
        "height": 260,
        "content": "## 获取工作流并筛选"
      },
      "typeVersion": 1
    },
    {
      "id": "add44468-ffba-401e-a119-3e25c4870ece",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -360,
        -120
      ],
      "parameters": {
        "color": 6,
        "width": 760,
        "height": 400,
        "content": "## 用于测试的 webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "1de3f20f-152c-4f81-b42d-cc9e03f5bf0d",
      "name": "响应",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        120,
        -40
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.1
    },
    {
      "id": "0a7a9d55-5b34-4129-952d-c982ebcc3d7c",
      "name": "HTML响应",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        180,
        460
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "={{$json.html}}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "f85d91a0-16ec-4f79-870a-dfc24f954469",
      "name": "生成完整HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        -40,
        460
      ],
      "parameters": {
        "html": "<html>\n<head>\n  <title>{{$('Configs').item.json.name_doc}}</title>\n  <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css\">\n  <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n  <style>\n    :root {\n      --bs-body-bg: #1a1a1a;\n      --bs-body-color: #e0e0e0;\n      --bs-border-color: #444;\n      --bs-accordion-bg: #2b2b2b;\n      --bs-accordion-color: #e0e0e0;\n      --bs-accordion-button-color: #e0e0e0;\n      --bs-accordion-border-color: #444;\n      --bs-accordion-button-active-bg: #3c3c3c;\n      --bs-accordion-button-active-color: #ffffff;\n      --bs-accordion-button-focus-border-color: #007bff;\n      --bs-accordion-button-focus-box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);\n    }\n    .method {\n      display: inline-block;\n      padding: 0.3em 0.8em;\n      border-radius: 0.25rem;\n      font-weight: bold;\n      font-size: 0.9em;\n      color: white;\n      text-transform: uppercase;\n      margin-right: 1rem;\n    }\n    .method-post { background-color: #49cc90; }\n    .method-get { background-color: #61affe; }\n    .endpoint-path {\n      font-family: monospace;\n      font-size: 1.1em;\n      color: #ccc;\n    }\n    pre {\n      background-color: #161B22;\n      padding: 1rem;\n      border-radius: 0.25rem;\n      white-space: pre-wrap;\n      word-break: break-all;\n      border: 1px solid var(--bs-border-color);\n    }\n    code { color: #c9d1d9; }\n    .accordion-button:not(.collapsed) {\n        box-shadow: inset 0 -1px 0 var(--bs-accordion-border-color);\n    }\n  </style>\n</head>\n<body class=\"p-4\">\n  <div class=\"container\">\n    <div class=\"d-flex align-items-baseline mb-4\">\n      <h1 class=\"text-white me-3\">{{$('Configs').item.json.name_doc}}</h1>\n      <p class=\"text-white-50 me-3\">{{$('Configs').item.json.description}}</p>\n      <span class=\"badge bg-secondary\">{{$('Configs').item.json.version}}</span>\n    </div>\n    \n    {{ $json.htmlContent || `\n<div class=\"alert alert-danger d-flex align-items-center mt-4\" role=\"alert\">\n  <i class=\"bi bi-x-octagon-fill me-3\" style=\"font-size: 2rem;\"></i>\n  <div>\n    <h4 class=\"alert-heading\">Documentation Generation Failed!</h4>\n    <p>An error occurred while trying to generate the API documentation from the sub-workflow.</p>\n    <hr>\n    <p class=\"mb-1\"><strong>Please check the following:</strong></p>\n    <ul>\n      <li>Ensure the sub-workflow (the one that generates the accordion) executed successfully and produced an output named <code>htmlContent</code>.</li>\n      <li>Verify that at least one of your workflows is active and contains a correctly configured <code>API_DOCS</code> node.</li>\n      <li>Check the n8n execution logs for more detailed error messages from the sub-workflow.</li>\n    </ul>\n  </div>\n</div>\n`}}\n\n\n    </div>\n <footer class=\"mt-5 pt-4 text-center text-white-50\">\n  <p>\n    Created by <a href=\"https://www.linkedin.com/in/matheus-pedrosa-custodio/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"link-light text-decoration-none\">\n      <i class=\"bi bi-linkedin\"></i> Matheus Pedrosa\n    </a>\n  </p>\n</footer>\n  \n  <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js\"></script>\n</body>\n</html>"
      },
      "typeVersion": 1.2
    },
    {
      "id": "0bd66854-cf1b-46b5-ad75-e0d7ef9b71d4",
      "name": "执行子工作流",
      "type": "n8n-nodes-base.executeWorkflow",
      "onError": "continueRegularOutput",
      "position": [
        -260,
        460
      ],
      "parameters": {
        "options": {
          "waitForSubWorkflow": true
        },
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "y2xOdk7RyRoS2sgF",
          "cachedResultName": "Documentation"
        },
        "workflowInputs": {
          "value": {},
          "schema": [
            {
              "id": "url_base",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "url_base",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "43a02f1b-9f18-4148-9705-2c3347997cfa",
      "name": "执行",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -320,
        -300
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "url_base"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "91ad9f92-adf3-4a8c-af48-3d1e5cd06678",
      "name": "获取工作流",
      "type": "n8n-nodes-base.n8n",
      "position": [
        -100,
        -300
      ],
      "parameters": {
        "filters": {
          "tags": "",
          "activeWorkflows": true
        },
        "requestOptions": {}
      },
      "credentials": {
        "n8nApi": {
          "id": "fTE1pX6nL40KSIdW",
          "name": "n8n account"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "c28819b2-2e5d-4732-9a68-0d5045dcb59e",
      "name": "筛选工作流",
      "type": "n8n-nodes-base.code",
      "position": [
        340,
        -300
      ],
      "parameters": {
        "jsCode": "const allWorkflows = $('Aggregate').last().json.data;\nlet html = '';\n\nconst documentedWebhookWorkflows = allWorkflows.filter(item => {\n  if (!item.nodes || item.nodes.length === 0) {\n    return false;\n  }\n  const hasWebhook = item.nodes.some(node => node.type === 'n8n-nodes-base.webhook');\n  const hasDocsNote = item.nodes.some(node => node.type === 'n8n-nodes-base.set' && node.name === 'API_DOCS');\n  return hasWebhook && hasDocsNote;\n});\n\n\nif (documentedWebhookWorkflows.length > 0) {\n  html += '<div class=\"accordion\" id=\"docsAccordion\">';\n\n  const n8nBaseUrl = $(\"Execute\").last().json.url_base || 'https://n8n.io/';\n\n  for (const workflow of documentedWebhookWorkflows) {\n    const docsNode = workflow.nodes.find(n => n.name === 'API_DOCS');\n\n    if (docsNode) {\n      const jsonString = docsNode.parameters.jsonOutput || JSON.stringify(docsNode.parameters.docsData);\n      let docsData = {};\n      let webhookNode = null;\n\n      try {\n        docsData = JSON.parse(jsonString);\n        webhookNode = workflow.nodes.find(n => { return n.type === 'n8n-nodes-base.webhook' && n.name === docsData.webhook });\n      } catch (e) {\n        console.error(`Erro ao parsear JSON no workflow: ${workflow.name}`, e);\n        continue;\n      }\n\n      if(webhookNode){\n        const method = (docsData.method || \"POST\").toUpperCase();\n        const uniqueId = webhookNode.parameters.path.replace(/[^a-zA-Z0-9]/g, '');\n\n        // --- make CURL\n        const fullUrl = `${n8nBaseUrl}webhook/${webhookNode.parameters.path}`;\n\n        let curlCommand = `curl -X ${method}\\\\`;\n        if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n          const requestBodyString = JSON.stringify(docsData.requestBody || {});\n          curlCommand += `\n          -H 'Content-Type: application/json' \\\\\n          -d '${requestBodyString}' \\\\`;\n                }\n                curlCommand += `\n          ${fullUrl}`;\n\n        html += `\n          <div class=\"accordion-item\">\n            <h2 class=\"accordion-header\" id=\"heading-${uniqueId}\">\n              <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#collapse-${uniqueId}\" aria-expanded=\"false\" aria-controls=\"collapse-${uniqueId}\">\n                <span class=\"method method-${method.toLowerCase()}\">${method}</span>\n                <span class=\"endpoint-path\">/webhook/${webhookNode.parameters.path}</span>\n              </button>\n            </h2>\n            <div id=\"collapse-${uniqueId}\" class=\"accordion-collapse collapse\" aria-labelledby=\"heading-${uniqueId}\" data-bs-parent=\"#docsAccordion\">\n              <div class=\"accordion-body\">\n                <p><strong>${docsData.summary || 'No summary'}</strong></p>\n                <p>${docsData.description || 'No description.'}</p>\n                <hr style=\"border-color: var(--bs-border-color);\">\n                \n                <h6>cURL Command:</h6>\n                <pre><code>${curlCommand}</code></pre>\n                \n                <h6>Request Body:</h6>\n                <pre><code>${JSON.stringify(docsData.requestBody || {}, null, 2)}</code></pre>\n                <h6>Success Response (${docsData.successCode || 'N/A'}):</h6>\n                <pre><code>${JSON.stringify(docsData.successResponse || {}, null, 2)}</code></pre>\n                <h6>Error Response (${docsData.errorCode || 'N/A'}):</h6>\n                <pre><code>${JSON.stringify(docsData.errorResponse || {}, null, 2)}</code></pre>\n              </div>\n            </div>\n          </div>\n        `;\n      }\n    }\n  }\n  html += '</div>';\n} else {\n  html += '<div class=\"alert alert-info\">Nenhum endpoint documentado foi encontrado.</div>';\n}\n\nreturn [{\n  json: {\n    htmlContent: html\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "78526a12-613c-4530-9a6f-bb612036addf",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -940,
        -400
      ],
      "parameters": {
        "color": 4,
        "width": 540,
        "height": 700,
        "content": "### 实时 n8n API 文档生成器"
      },
      "typeVersion": 1
    },
    {
      "id": "602d0cd7-558e-4360-b385-bf9824f9278b",
      "name": "Webhook1",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -320,
        -40
      ],
      "webhookId": "15105da5-96ca-472f-a7db-2ddc2e8b0891",
      "parameters": {
        "path": "15105da5-96ca-472f-a7db-2ddc2e8b0891",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "6283f30d-d1eb-4a7b-9f97-73834d7c060f",
      "name": "API_DOCS",
      "type": "n8n-nodes-base.set",
      "position": [
        -320,
        120
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"expose\": true,\n  \"webhook\": \"Webhook1\",\n  \"summary\": \"Title test\",\n  \"method\":\"get\",\n  \"description\": \"Hey community, look at this cool workflow I'm doing.\",\n  \"tags\": [\n    \"test\",\n    \"tag_0101\"\n  ],\n  \"requestBody\": {},\n  \"successCode\": 200,\n  \"successResponse\": {\n    \"status\": 200,\n    \"message\": \"Workflow start\"\n  },\n  \"errorCode\": 400,\n  \"errorResponse\": {\n    \"status\": 500,\n    \"message\": \"Workflow error\"\n  }\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "57843c34-afb5-401e-86dc-8aa22c242263",
      "name": "模拟响应",
      "type": "n8n-nodes-base.code",
      "position": [
        -100,
        -40
      ],
      "parameters": {
        "jsCode": "return {\n    \"status\": 200,\n    \"message\": \"Workflow start\"\n  }"
      },
      "typeVersion": 2
    },
    {
      "id": "29b6b6ea-917b-47a3-b8a1-ff017b0a10a5",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -940,
        400
      ],
      "parameters": {
        "width": 150,
        "height": 200,
        "content": "### 创建文档页面"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Configs": {
      "main": [
        [
          {
            "node": "ExecuteSubWorkflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute": {
      "main": [
        [
          {
            "node": "GetWorkflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Configs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook1": {
      "main": [
        [
          {
            "node": "FakeResponse",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "FilterWorkflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FakeResponse": {
      "main": [
        [
          {
            "node": "Respond",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GetWorkflows": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MakeFullHTML": {
      "main": [
        [
          {
            "node": "RespondHTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FilterWorkflows": {
      "main": [
        []
      ]
    },
    "ExecuteSubWorkflow": {
      "main": [
        [
          {
            "node": "MakeFullHTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 文档提取, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量17
分类2
节点类型10
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Matheus Pedrosa

Matheus Pedrosa

@julinho

I am a software engineer specializing in automations, with extensive experience on the N8N platform. With solid skills in JavaScript, Go, .NET, and C#, I am equipped to develop efficient and scalable solutions.

外部链接
在 n8n.io 查看

分享此工作流