mcp-swagger-server/docs/backend-api-service-impleme...

21 KiB
Raw Blame History

MCP Swagger 后端 API 服务实施方案

📋 方案概述

将 OpenAPI/Swagger 解析功能从前端提取为独立的后端 API 服务,实现前后端彻底分离,提升系统架构的可维护性、可扩展性和性能。

🎯 核心优势

1. 架构优势

  • 关注点分离: 前端专注UI交互后端专注业务逻辑
  • 技术栈解耦: 前端无需处理复杂的Node.js依赖
  • 独立部署: 前后端可以独立构建、部署和扩展
  • 版本管理: API版本化管理支持向后兼容

2. 性能优势

  • 服务端解析: 避免大型解析库在浏览器中加载
  • 缓存机制: 服务端可实现智能缓存策略
  • 并发处理: 服务端可处理多个并发解析请求
  • 资源优化: 减少前端包体积

3. 开发优势

  • 调试便利: 后端逻辑更容易调试和监控
  • 测试友好: API接口更容易进行单元测试和集成测试
  • 扩展性强: 可轻松添加新的解析格式和功能

🏗️ 技术架构设计

整体架构图

┌─────────────────────────────────────────────────────────────┐
│                    前端层 (Vue 3 + Vite)                    │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │   UI组件    │ │   状态管理  │ │   HTTP客户端│           │
│  │ Naive UI    │ │   Pinia     │ │    Axios    │           │
│  │ 表单/展示   │ │   响应式    │ │   拦截器    │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────┼─────────────────────────────────┘
                          │ HTTP REST API
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                  API网关层 (Express.js)                     │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │   路由管理  │ │   中间件    │ │   错误处理  │           │
│  │ RESTful API │ │    CORS     │ │   统一响应  │           │
│  │   参数验证  │ │  Body Parser│ │   日志记录  │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────┼─────────────────────────────────┘
                          │ 业务逻辑调用
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                业务服务层 (Service Layer)                   │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │ 解析服务    │ │ 转换服务    │ │ 验证服务    │           │
│  │ ParserService│ │ConvertService│ │ValidateService│        │
│  │ 多格式支持  │ │  MCP转换    │ │  规范检查   │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────┼─────────────────────────────────┘
                          │ 核心库调用
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                核心库层 (mcp-swagger-parser)               │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │   解析器    │ │   验证器    │ │   转换器    │           │
│  │  多格式解析 │ │  Schema校验 │ │  格式转换   │           │
│  │  错误处理   │ │  结构验证   │ │  数据映射   │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────────────────────────────────────────┘

🛠️ 实施计划

阶段一: 后端API服务搭建 (1-2天)

1.1 创建独立的API服务模块

在现有的 monorepo 结构中新增:

packages/
├── mcp-swagger-api/                 # 🆕 新增API服务
│   ├── src/
│   │   ├── app.ts                   # Express应用入口
│   │   ├── server.ts                # 服务器启动文件
│   │   ├── routes/                  # 路由定义
│   │   │   ├── index.ts
│   │   │   ├── parse.ts             # 解析相关路由
│   │   │   ├── convert.ts           # 转换相关路由
│   │   │   └── validate.ts          # 验证相关路由
│   │   ├── services/                # 业务服务层
│   │   │   ├── parser.service.ts
│   │   │   ├── converter.service.ts
│   │   │   └── validator.service.ts
│   │   ├── middlewares/             # 中间件
│   │   │   ├── error-handler.ts
│   │   │   ├── cors.ts
│   │   │   └── validation.ts
│   │   ├── types/                   # 类型定义
│   │   │   ├── api.ts
│   │   │   └── request.ts
│   │   └── utils/                   # 工具函数
│   │       ├── response.ts
│   │       └── logger.ts
│   ├── package.json
│   ├── tsconfig.json
│   └── README.md

1.2 API接口设计

核心API端点设计
// 1. 解析接口
POST /api/v1/parse
{
  "source": {
    "type": "url" | "file" | "text",
    "content": string,
    "encoding": "utf-8" | "base64"
  },
  "options": {
    "strictMode": boolean,
    "resolveReferences": boolean,
    "validateSchema": boolean
  }
}

// 2. 验证接口  
POST /api/v1/validate
{
  "source": {
    "type": "url" | "file" | "text", 
    "content": string
  },
  "validationLevel": "basic" | "strict" | "extended"
}

// 3. 转换接口
POST /api/v1/convert
{
  "source": {
    "type": "url" | "file" | "text",
    "content": string
  },
  "config": {
    "outputFormat": "json" | "yaml",
    "includeExamples": boolean,
    "groupByTags": boolean,
    "customSettings": object
  }
}

// 4. 健康检查
GET /api/v1/health

// 5. 服务信息
GET /api/v1/info
统一响应格式
interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: any;
  };
  meta?: {
    timestamp: string;
    requestId: string;
    duration: number;
  };
}

阶段二: 服务实现 (2-3天)

2.1 Express应用基础架构

// src/app.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';
import { errorHandler } from './middlewares/error-handler';
import { requestLogger } from './middlewares/logger';
import routes from './routes';

export function createApp() {
  const app = express();
  
  // 安全中间件
  app.use(helmet());
  app.use(cors({
    origin: process.env.FRONTEND_URL || 'http://localhost:5173',
    credentials: true
  }));
  
  // 基础中间件
  app.use(compression());
  app.use(express.json({ limit: '10mb' }));
  app.use(express.urlencoded({ extended: true }));
  
  // 日志中间件
  app.use(requestLogger);
  
  // API路由
  app.use('/api/v1', routes);
  
  // 错误处理
  app.use(errorHandler);
  
  return app;
}

2.2 核心服务实现

// src/services/parser.service.ts
import { parseFromUrl, parseFromFile, parseFromString } from 'mcp-swagger-parser';
import type { InputSource, ParseOptions, ParseResult } from '../types/api';

export class ParserService {
  async parse(source: InputSource, options: ParseOptions): Promise<ParseResult> {
    try {
      let result;
      
      switch (source.type) {
        case 'url':
          result = await parseFromUrl(source.content, options);
          break;
        case 'file':
          result = await this.parseFromBase64File(source.content, options);
          break;
        case 'text':
          result = await parseFromString(source.content, options);
          break;
        default:
          throw new Error(`Unsupported source type: ${source.type}`);
      }
      
      return {
        success: true,
        spec: result.spec,
        apiInfo: this.extractApiInfo(result.spec),
        endpoints: this.extractEndpoints(result.spec),
        statistics: this.generateStatistics(result.spec)
      };
      
    } catch (error) {
      throw new ParserError(`Parse failed: ${error.message}`, 'PARSE_ERROR');
    }
  }
  
  private async parseFromBase64File(base64Content: string, options: ParseOptions) {
    const buffer = Buffer.from(base64Content, 'base64');
    const content = buffer.toString('utf-8');
    return await parseFromString(content, options);
  }
  
  private extractApiInfo(spec: any) {
    return {
      title: spec.info?.title || 'Untitled API',
      version: spec.info?.version || '1.0.0',
      description: spec.info?.description,
      serverUrl: spec.servers?.[0]?.url,
      totalEndpoints: Object.keys(spec.paths || {}).length
    };
  }
  
  private extractEndpoints(spec: any) {
    // 端点提取逻辑
  }
  
  private generateStatistics(spec: any) {
    // 统计信息生成逻辑
  }
}

2.3 路由控制器实现

// src/routes/parse.ts
import { Router } from 'express';
import { ParserService } from '../services/parser.service';
import { validateRequest } from '../middlewares/validation';
import { asyncHandler } from '../utils/async-handler';

const router = Router();
const parserService = new ParserService();

router.post('/parse', 
  validateRequest('parseRequest'),
  asyncHandler(async (req, res) => {
    const { source, options = {} } = req.body;
    
    const result = await parserService.parse(source, options);
    
    res.json({
      success: true,
      data: result,
      meta: {
        timestamp: new Date().toISOString(),
        requestId: req.id,
        duration: Date.now() - req.startTime
      }
    });
  })
);

export default router;

阶段三: 前端适配 (1-2天)

3.1 API客户端封装

// src/api/client.ts
import axios, { AxiosInstance, AxiosResponse } from 'axios';
import type { ApiResponse, ParseRequest, ParseResult } from '@/types/api';

class ApiClient {
  private client: AxiosInstance;
  
  constructor() {
    this.client = axios.create({
      baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001/api/v1',
      timeout: 30000,
      headers: {
        'Content-Type': 'application/json',
      },
    });
    
    this.setupInterceptors();
  }
  
  private setupInterceptors() {
    // 请求拦截器
    this.client.interceptors.request.use(
      (config) => {
        console.log(`🚀 API Request: ${config.method?.toUpperCase()} ${config.url}`);
        return config;
      },
      (error) => Promise.reject(error)
    );
    
    // 响应拦截器
    this.client.interceptors.response.use(
      (response: AxiosResponse<ApiResponse>) => {
        if (!response.data.success) {
          throw new Error(response.data.error?.message || 'API request failed');
        }
        return response;
      },
      (error) => {
        console.error('❌ API Error:', error.response?.data || error.message);
        return Promise.reject(error);
      }
    );
  }
  
  async parse(request: ParseRequest): Promise<ParseResult> {
    const response = await this.client.post<ApiResponse<ParseResult>>('/parse', request);
    return response.data.data!;
  }
  
  async validate(request: ValidateRequest): Promise<ValidationResult> {
    const response = await this.client.post<ApiResponse<ValidationResult>>('/validate', request);
    return response.data.data!;
  }
  
  async convert(request: ConvertRequest): Promise<ConvertResult> {
    const response = await this.client.post<ApiResponse<ConvertResult>>('/convert', request);
    return response.data.data!;
  }
  
  async healthCheck(): Promise<{ status: 'ok' | 'error', timestamp: string }> {
    const response = await this.client.get<ApiResponse>('/health');
    return response.data.data!;
  }
}

export const apiClient = new ApiClient();

3.2 前端服务层改造

// src/services/parser.service.ts (前端)
import { apiClient } from '@/api/client';
import type { InputSource, ConvertConfig, OpenApiInfo, ApiEndpoint, ConvertResult } from '@/types';

export class ParserService {
  /**
   * 验证 OpenAPI 规范
   */
  async validateOpenAPISpec(source: InputSource): Promise<ValidationResult> {
    try {
      return await apiClient.validate({
        source,
        validationLevel: 'strict'
      });
    } catch (error) {
      console.error('验证失败:', error);
      throw new ParserError(`验证失败: ${error.message}`, 'VALIDATION_ERROR');
    }
  }
  
  /**
   * 解析 OpenAPI 规范获取基本信息
   */
  async parseApiInfo(source: InputSource): Promise<OpenApiInfo> {
    try {
      const result = await apiClient.parse({
        source,
        options: {
          strictMode: false,
          resolveReferences: true,
          validateSchema: true
        }
      });
      
      return result.apiInfo;
    } catch (error) {
      console.error('解析失败:', error);
      throw new ParserError(`解析失败: ${error.message}`, 'PARSE_ERROR');
    }
  }
  
  /**
   * 解析端点信息
   */
  async parseEndpoints(source: InputSource): Promise<ApiEndpoint[]> {
    try {
      const result = await apiClient.parse({
        source,
        options: {
          strictMode: false,
          resolveReferences: true,
          validateSchema: true
        }
      });
      
      return result.endpoints;
    } catch (error) {
      console.error('端点解析失败:', error);
      throw new ParserError(`端点解析失败: ${error.message}`, 'ENDPOINT_PARSE_ERROR');
    }
  }
  
  /**
   * 转换为 MCP 格式
   */
  async convertToMcp(source: InputSource, config: ConvertConfig): Promise<ConvertResult> {
    try {
      return await apiClient.convert({
        source,
        config
      });
    } catch (error) {
      console.error('转换失败:', error);
      throw new ParserError(`转换失败: ${error.message}`, 'CONVERT_ERROR');
    }
  }
}

export const parserService = new ParserService();

阶段四: 部署配置 (1天)

4.1 开发环境配置

// packages/mcp-swagger-api/src/config/development.ts
export default {
  port: 3001,
  cors: {
    origin: ['http://localhost:5173', 'http://localhost:3000'],
    credentials: true
  },
  logging: {
    level: 'debug',
    format: 'dev'
  },
  cache: {
    enabled: false
  }
};

4.2 生产环境配置

// packages/mcp-swagger-api/src/config/production.ts
export default {
  port: process.env.PORT || 3001,
  cors: {
    origin: process.env.FRONTEND_URL?.split(',') || ['https://yourdomain.com'],
    credentials: true
  },
  logging: {
    level: 'info',
    format: 'combined'
  },
  cache: {
    enabled: true,
    ttl: 300 // 5分钟
  }
};

4.3 Docker配置

# packages/mcp-swagger-api/Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY dist ./dist

EXPOSE 3001

CMD ["node", "dist/server.js"]

阶段五: 测试与优化 (1-2天)

5.1 API测试

// packages/mcp-swagger-api/tests/api.test.ts
import request from 'supertest';
import { createApp } from '../src/app';

describe('Parser API', () => {
  const app = createApp();
  
  describe('POST /api/v1/parse', () => {
    it('should parse OpenAPI spec from URL', async () => {
      const response = await request(app)
        .post('/api/v1/parse')
        .send({
          source: {
            type: 'url',
            content: 'https://petstore.swagger.io/v2/swagger.json'
          },
          options: {
            strictMode: false,
            resolveReferences: true
          }
        })
        .expect(200);
        
      expect(response.body.success).toBe(true);
      expect(response.body.data.apiInfo).toBeDefined();
      expect(response.body.data.endpoints).toBeInstanceOf(Array);
    });
  });
});

5.2 性能优化

// src/middlewares/cache.ts
import NodeCache from 'node-cache';

const cache = new NodeCache({ 
  stdTTL: 300, // 5分钟默认缓存
  checkperiod: 60 // 每分钟清理过期缓存
});

export function cacheMiddleware(ttl?: number) {
  return (req: Request, res: Response, next: NextFunction) => {
    const key = `${req.method}:${req.url}:${JSON.stringify(req.body)}`;
    const cached = cache.get(key);
    
    if (cached) {
      console.log(`🎯 Cache hit: ${key}`);
      return res.json(cached);
    }
    
    const originalSend = res.json;
    res.json = function(data) {
      if (res.statusCode === 200) {
        cache.set(key, data, ttl);
        console.log(`💾 Cache set: ${key}`);
      }
      return originalSend.call(this, data);
    };
    
    next();
  };
}

🔧 脚本与工具

package.json 脚本配置

{
  "scripts": {
    "dev:api": "pnpm --filter=mcp-swagger-api run dev",
    "dev:ui": "pnpm --filter=mcp-swagger-ui run dev", 
    "dev:full": "concurrently \"pnpm run dev:api\" \"pnpm run dev:ui\"",
    "build:api": "pnpm --filter=mcp-swagger-api run build",
    "build:ui": "pnpm --filter=mcp-swagger-ui run build",
    "test:api": "pnpm --filter=mcp-swagger-api run test",
    "test:ui": "pnpm --filter=mcp-swagger-ui run test"
  }
}

开发启动脚本

#!/bin/bash
# scripts/dev-full.sh

echo "🚀 启动全栈开发环境..."

# 启动API服务
echo "📡 启动API服务..."
pnpm --filter=mcp-swagger-api run dev &
API_PID=$!

# 等待API服务启动
sleep 3

# 启动前端服务
echo "🎨 启动前端服务..."
pnpm --filter=mcp-swagger-ui run dev &
UI_PID=$!

# 等待用户输入退出
echo "✅ 开发环境已启动"
echo "   - API服务: http://localhost:3001"
echo "   - 前端服务: http://localhost:5173"
echo ""
echo "按 Ctrl+C 退出..."

# 捕获退出信号,清理进程
trap "kill $API_PID $UI_PID; exit" INT TERM

wait

📊 迁移对比

改造前 (当前架构)

前端 (Vue 3)
├── 直接引用 mcp-swagger-parser
├── 浏览器中执行解析逻辑  ❌
├── 大量Node.js依赖打包  ❌ 
└── 解析错误调试困难    ❌

改造后 (目标架构)

前端 (Vue 3)
├── HTTP API调用
├── 轻量级客户端        ✅
└── 清晰的错误处理      ✅

后端 API服务 (Express)
├── 专业的解析服务      ✅
├── 缓存和性能优化      ✅
├── 完整的错误处理      ✅
└── 独立测试和部署      ✅

🎯 实施建议

  1. 渐进式迁移: 可以先保留现有的mock模式新增API模式作为选项
  2. 向后兼容: 保持现有的接口不变内部实现切换到API调用
  3. 错误处理: 当API服务不可用时自动降级到mock模式
  4. 监控告警: 添加API服务的健康检查和监控

📈 预期收益

  1. 开发效率: 前后端独立开发,提升并行开发效率
  2. 维护成本: 清晰的架构边界,降低长期维护成本
  3. 扩展能力: 后端服务可独立扩展,支持更多客户端
  4. 用户体验: 更快的加载速度和更稳定的解析性能

这个方案充分利用了现有的 monorepo 架构和 mcp-swagger-parser 核心库,是一个既务实又具有前瞻性的技术方案。