Python案例如何实现接口文档生成?

wen python案例 50

本文目录导读:

Python案例如何实现接口文档生成?

  1. 使用 FastAPI + Swagger UI(推荐)
  2. 使用 Flask-RESTx
  3. 使用 Django REST Framework + Swagger
  4. 使用 APIDOC 生成静态文档
  5. 使用 Connexion + OpenAPI
  6. 生成 Markdown 格式的静态文档
  7. 安装依赖
  8. 运行方式
  9. 推荐方案

Python实现接口文档生成有多种方式,我为你介绍几种常用的方法:

使用 FastAPI + Swagger UI(推荐)

FastAPI 自动生成 OpenAPI 规范的接口文档:

from fastapi import FastAPI, Query, Path, Body
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
# 创建 FastAPI 实例
app = FastAPI("用户管理系统 API",
    description="这是一个用户管理系统的接口文档",
    version="1.0.0",
    docs_url="/docs",  # Swagger UI 路径
    redoc_url="/redoc"  # ReDoc 路径
)
# 定义数据模型
class User(BaseModel):
    username: str
    email: str
    full_name: Optional[str] = None
    age: Optional[int] = None
    created_at: datetime = datetime.now()
class UserCreate(BaseModel):
    username: str = Field(..., min_length=3, max_length=50, description="用户名")
    email: str = Field(..., description="邮箱地址")
    full_name: Optional[str] = Field(None, description="用户全名")
    password: str = Field(..., min_length=6, description="密码")
# 示例数据
users_db = {}
@app.get("/")
async def root():
    """
    根路径,返回欢迎信息
    """
    return {"message": "欢迎使用用户管理系统 API"}
@app.get("/users", 
         tags=["用户管理"],
         summary="获取所有用户",
         description="返回系统中所有用户的列表")
async def get_users(
    skip: int = Query(0, description="跳过的记录数"),
    limit: int = Query(10, description="返回的记录数")
):
    """
    获取用户列表
    - **skip**: 跳过的记录数,默认为0
    - **limit**: 返回的记录数,默认为10
    """
    users = list(users_db.values())[skip:skip + limit]
    return {"total": len(users_db), "users": users}
@app.post("/users", 
          tags=["用户管理"],
          summary="创建用户",
          response_model=User,
          status_code=201)
async def create_user(user: UserCreate):
    """
    创建一个新用户
    - **username**: 用户名(必填,3-50个字符)
    - **email**: 邮箱地址(必填)
    - **full_name**: 用户全名(可选)
    - **password**: 密码(必填,至少6个字符)
    """
    if user.username in users_db:
        return {"error": "用户已存在"}
    new_user = User(
        username=user.username,
        email=user.email,
        full_name=user.full_name
    )
    users_db[user.username] = new_user
    return new_user
@app.get("/users/{user_id}", 
         tags=["用户管理"],
         summary="获取指定用户")
async def get_user(
    user_id: str = Path(..., description="用户ID"),
    detail: bool = Query(False, description="是否返回详细信息")
):
    """
    根据用户ID获取用户信息
    """
    if user_id not in users_db:
        return {"error": "用户不存在"}
    user = users_db[user_id]
    if detail:
        return {"user": user, "detail": True}
    return {"user": user}
@app.put("/users/{user_id}", 
         tags=["用户管理"],
         summary="更新用户信息")
async def update_user(
    user_id: str = Path(..., description="用户ID"),
    user: UserCreate = Body(..., description="更新的用户信息")
):
    """
    更新指定用户的信息
    """
    if user_id not in users_db:
        return {"error": "用户不存在"}
    updated_user = User(
        username=user.username,
        email=user.email,
        full_name=user.full_name
    )
    users_db[user_id] = updated_user
    return {"message": "用户更新成功", "user": updated_user}
@app.delete("/users/{user_id}", 
            tags=["用户管理"],
            summary="删除用户")
async def delete_user(
    user_id: str = Path(..., description="用户ID")
):
    """
    删除指定用户
    """
    if user_id not in users_db:
        return {"error": "用户不存在"}
    del users_db[user_id]
    return {"message": "用户删除成功"}
# 运行服务
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

使用 Flask-RESTx

Flask的扩展,可以生成Swagger文档:

from flask import Flask
from flask_restx import Api, Resource, fields, reqparse
from datetime import datetime
app = Flask(__name__)
# 创建 API 实例
api = Api(
    app,
    version='1.0','商品管理系统 API',
    description='商品管理系统的接口文档',
    doc='/docs/'  # Swagger UI 路径
)
# 定义命名空间
ns = api.namespace('products', description='商品操作')
# 定义数据模型
product_model = api.model('Product', {
    'id': fields.Integer(readonly=True, description='商品ID'),
    'name': fields.String(required=True, description='商品名称'),
    'price': fields.Float(required=True, description='商品价格'),
    'category': fields.String(description='商品分类'),
    'created_at': fields.DateTime(description='创建时间')
})
product_input = api.model('ProductInput', {
    'name': fields.String(required=True, description='商品名称'),
    'price': fields.Float(required=True, description='商品价格'),
    'category': fields.String(description='商品分类')
})
# 模拟数据库
products = {}
next_id = 1
@ns.route('/')
class ProductList(Resource):
    @ns.doc('list_products')
    @ns.marshal_list_with(product_model)
    def get(self):
        """获取所有商品"""
        return list(products.values())
    @ns.doc('create_product')
    @ns.expect(product_input)
    @ns.marshal_with(product_model, code=201)
    def post(self):
        """创建新商品"""
        global next_id
        data = api.payload
        product = {
            'id': next_id,
            'name': data['name'],
            'price': data['price'],
            'category': data.get('category', ''),
            'created_at': datetime.now()
        }
        products[next_id] = product
        next_id += 1
        return product, 201
@ns.route('/<int:id>')
@ns.response(404, '商品不存在')
@ns.param('id', '商品ID')
class Product(Resource):
    @ns.doc('get_product')
    @ns.marshal_with(product_model)
    def get(self, id):
        """获取指定商品"""
        if id not in products:
            api.abort(404)
        return products[id]
    @ns.doc('update_product')
    @ns.expect(product_input)
    @ns.marshal_with(product_model)
    def put(self, id):
        """更新商品信息"""
        if id not in products:
            api.abort(404)
        data = api.payload
        products[id].update({
            'name': data['name'],
            'price': data['price'],
            'category': data.get('category', '')
        })
        return products[id]
    @ns.doc('delete_product')
    @ns.response(204, '商品已删除')
    def delete(self, id):
        """删除商品"""
        if id not in products:
            api.abort(404)
        del products[id]
        return '', 204
if __name__ == '__main__':
    app.run(debug=True, port=5000)

使用 Django REST Framework + Swagger

# settings.py
INSTALLED_APPS = [
    'rest_framework',
    'drf_yasg',  # Django REST Swagger
    # ...
]
# urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
    openapi.Info(
        title="博客系统 API",
        default_version='v1',
        description="博客系统的API接口文档",
        terms_of_service="https://www.example.com/terms/",
        contact=openapi.Contact(email="contact@example.com"),
        license=openapi.License(name="BSD License"),
    ),
    public=True,
    permission_classes=[permissions.AllowAny],
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('blog.urls')),
    # Swagger UI
    path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    # ReDoc
    path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
# blog/views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    @swagger_auto_schema(
        operation_description="获取所有文章列表",
        operation_summary="文章列表",
        manual_parameters=[
            openapi.Parameter(
                'category',
                openapi.IN_QUERY,
                description="按分类筛选",
                type=openapi.TYPE_STRING
            ),
            openapi.Parameter(
                'search',
                openapi.IN_QUERY,
                description="搜索关键词",
                type=openapi.TYPE_STRING
            ),
        ],
        responses={
            200: PostSerializer(many=True),
            400: "请求参数错误"
        }
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
    @swagger_auto_schema(
        operation_description="创建新文章",
        operation_summary="创建文章",
        request_body=PostSerializer,
        responses={
            201: PostSerializer(),
            400: "数据验证失败"
        }
    )
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

使用 APIDOC 生成静态文档

// apidoc.json
{
  "name": "示例API",
  "version": "1.0.0",
  "description": "这是一个示例API文档",: "API文档",
  "url": "http://api.example.com"
}
// app.py - 添加注释
"""
@api {get} /api/users 获取用户列表
@apiName GetUsers
@apiGroup User
@apiVersion 1.0.0
@apiParam {Number} [page=1] 页码
@apiParam {Number} [limit=10] 每页数量
@apiSuccess {Number} code 状态码
@apiSuccess {String} message 提示信息
@apiSuccess {Object[]} data 用户列表
@apiSuccess {Number} data.id 用户ID
@apiSuccess {String} data.name 用户名
@apiSuccess {String} data.email 邮箱
@apiSuccessExample {json} 成功响应:
{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1,
            "name": "张三",
            "email": "zhangsan@example.com"
        }
    ]
}
@apiError {Number} code 错误码
@apiError {String} message 错误信息
@apiErrorExample {json} 错误响应:
{
    "code": 404,
    "message": "用户不存在"
}
"""

使用 Connexion + OpenAPI

# openapi.yaml
openapi: "3.0.0"
info: 任务管理系统
  version: "1.0.0"
  description: 任务管理系统的API文档
paths:
  /tasks:
    get:
      summary: 获取所有任务
      operationId: app.get_tasks
      parameters:
        - name: status
          in: query
          schema:
            type: string
          description: 任务状态
      responses:
        '200':
          description: 成功获取任务列表
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Task'
    post:
      summary: 创建新任务
      operationId: app.create_task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskInput'
      responses:
        '201':
          description: 任务创建成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
  /tasks/{task_id}:
    get:
      summary: 获取指定任务
      operationId: app.get_task
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 成功获取任务
components:
  schemas:
    Task:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
        status:
          type: string
          enum: [todo, in_progress, done]
        created_at:
          type: string
          format: date-time
    TaskInput:
      type: object
      required:
        - title
      properties:
        title:
          type: string
          description: 任务标题
        description:
          type: string
          description: 任务描述
        status:
          type: string
          default: todo
# app.py
import connexion
from datetime import datetime
app = connexion.FlaskApp(__name__)
app.add_api('openapi.yaml')
tasks = {}
next_id = 1
def get_tasks(status=None):
    """获取所有任务"""
    if status:
        filtered = [t for t in tasks.values() if t['status'] == status]
        return filtered
    return list(tasks.values())
def create_task(body):
    """创建新任务"""
    global next_id
    task = {
        'id': next_id,
        'title': body['title'],
        'description': body.get('description', ''),
        'status': body.get('status', 'todo'),
        'created_at': datetime.now().isoformat()
    }
    tasks[next_id] = task
    next_id += 1
    return task, 201
def get_task(task_id):
    """获取指定任务"""
    return tasks.get(task_id, {'error': '任务不存在'})
if __name__ == '__main__':
    app.run(port=8080)

生成 Markdown 格式的静态文档

# docs_generator.py
from typing import Dict, List
import json
class APIDocGenerator:
    def __init__(self, title: str, version: str, description: str):
        self.title = title
        self.version = version
        self.description = description
        self.endpoints = []
    def add_endpoint(self, method: str, path: str, summary: str, description: str, 
                    parameters: List[Dict] = None, request_body: Dict = None, 
                    responses: Dict = None):
        """添加API端点"""
        endpoint = {
            'method': method,
            'path': path,
            'summary': summary,
            'description': description,
            'parameters': parameters or [],
            'request_body': request_body,
            'responses': responses or {}
        }
        self.endpoints.append(endpoint)
    def generate_markdown(self) -> str:
        """生成Markdown格式文档"""
        md = f"""# {self.title}
> API版本: {self.version}
{self.description}
## 接口列表
"""
        for endpoint in self.endpoints:
            md += f"""### {endpoint['method']} {endpoint['path']}
**{endpoint['summary']}**
{endpoint['description']}
"""
            if endpoint['parameters']:
                md += "#### 请求参数\n\n"
                md += "| 参数名 | 类型 | 必填 | 描述 |\n"
                md += "|--------|------|------|------|\n"
                for param in endpoint['parameters']:
                    required = "是" if param.get('required') else "否"
                    md += f"| {param['name']} | {param['type']} | {required} | {param['description']} |\n"
                md += "\n"
            if endpoint['request_body']:
                md += "#### 请求体\n\n```json\n"
                md += json.dumps(endpoint['request_body'], indent=2)
                md += "\n```\n\n"
            if endpoint['responses']:
                md += "#### 响应示例\n\n"
                for status, example in endpoint['responses'].items():
                    md += f"**{status}**\n\n```json\n"
                    md += json.dumps(example, indent=2)
                    md += "\n```\n\n"
        return md
# 使用示例
if __name__ == "__main__":
    doc = APIDocGenerator(
        title="文件管理系统 API",
        version="1.0.0",
        description="文件上传、下载、管理等接口"
    )
    doc.add_endpoint(
        method="GET",
        path="/api/files",
        summary="获取文件列表",
        description="获取用户所有文件的列表",
        parameters=[
            {"name": "page", "type": "integer", "required": False, "description": "页码"},
            {"name": "limit", "type": "integer", "required": False, "description": "每页数量"}
        ],
        responses={
            200: {"files": [{"id": 1, "name": "file.txt", "size": 1024}], "total": 100}
        }
    )
    doc.add_endpoint(
        method="POST",
        path="/api/upload",
        summary="上传文件",
        description="上传文件到服务器",
        request_body={"file": "文件二进制数据", "type": "file"},
        responses={
            201: {"id": 1, "url": "http://example.com/file.txt"}
        }
    )
    markdown = doc.generate_markdown()
    with open("api_documentation.md", "w", encoding="utf-8") as f:
        f.write(markdown)
    print("文档已生成: api_documentation.md")

安装依赖

根据不同方案安装对应的依赖:

# FastAPI
pip install fastapi uvicorn
# Flask-RESTx
pip install flask flask-restx
# Django REST Framework + Swagger
pip install djangorestframework drf-yasg
# APIDOC (需要Node.js)
npm install -g apidoc
# Connexion
pip install connexion[swagger-ui]

运行方式

# FastAPI
python fastapi_app.py
# 访问 http://localhost:8000/docs (Swagger UI)
# 访问 http://localhost:8000/redoc (ReDoc)
# Flask-RESTx
python flask_app.py
# 访问 http://localhost:5000/docs/
# Django
python manage.py runserver
# 访问 http://localhost:8000/swagger/ (Swagger UI)
# 访问 http://localhost:8000/redoc/ (ReDoc)
# APIDOC
apidoc -i app.py -o docs/
# 在浏览器打开 docs/index.html
# Connexion
python connexion_app.py
# 访问 http://localhost:8080/ui/

推荐方案

  1. 新项目: 推荐使用 FastAPI + Swagger UI,自动生成文档,简洁高效
  2. 已有Flask项目: 使用 Flask-RESTxConnexion
  3. Django项目: 使用 Django REST Framework + drf-yasg
  4. 纯文档生成: 使用 APIDOC 或自定义Markdown生成器

这些方案都能生成美观、交互式的API文档,方便前后端协作开发和API测试。

抱歉,评论功能暂时关闭!