专栏系列04(模块1第4篇) 《蓝图系统与中间件帝国:40+个路由模块的组织哲学》

《蓝图系统与中间件帝国:40+个路由模块的组织哲学》

关于《与AI Agent同行:门户网站创建之旅经典技术分享》专栏

本专栏是一套系统性的Web开发技术实战教程,基于Madechango.com门户网站的真实开发经验,涵盖架构设计、AI能力集成、研究工具开发等9大模块共40篇文章。面向中高级Python开发者,通过18万行生产级代码实践,深入讲解Flask+FastAPI双轨架构、多模型AI矩阵、学术研究全链路工具等现代Web技术栈的完整应用。

摘要:本文是《与AI Agent同行:门户网站创建之旅经典技术分享》专栏系列第4篇(模块1第4篇),深入解析了Madechango.com项目中Flask蓝图系统的架构设计理念与实现细节。通过40+个路由模块的精细化组织,我们实现了功能边界清晰、代码结构优雅的模块化架构。文章详细介绍了蓝图系统设计原则、中间件生态系统构建、安全装饰器链的演进,以及如何通过URL扁平化设计将路由长度减少14.3%。同时分享了从app/routes到250+个API端点的成长历程和RESTful API规范的最佳实践。

核心亮点: 模块化架构设计

蓝图系统设计:功能模块的清晰边界

Madechango.com采用分层蓝图架构,将复杂的功能模块进行合理拆分:

python 复制代码
# 主应用蓝图结构
app/
├── routes/                    # 路由层
│   ├── __init__.py           # 主蓝图注册
│   ├── main.py              # 核心路由
│   ├── auth.py              # 认证相关
│   ├── user/                # 用户模块
│   │   ├── __init__.py
│   │   ├── profile.py
│   │   ├── settings.py
│   │   └── dashboard.py
│   ├── tools/               # 工具箱模块
│   │   ├── __init__.py
│   │   ├── flowchart.py
│   │   ├── apa_verify.py
│   │   └── document_converter.py
│   ├── research/            # 研究工具模块
│   │   ├── __init__.py
│   │   ├── qualitative.py   # ChangoVivo
│   │   ├── bibliometric.py  # ChangoBibVis
│   │   ├── paper_query.py   # ChangoPaperQuery
│   │   └── statistics.py    # ChangoPSS
│   └── class_space/         # 班级空间模块
│       ├── __init__.py
│       ├── management.py
│       ├── album.py
│       └── chat.py

# 蓝图注册示例
def register_blueprints(app):
    """注册所有蓝图"""
    
    # 核心蓝图
    from app.routes.main import main_bp
    from app.routes.auth import auth_bp
    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp, url_prefix='/auth')
    
    # 用户相关蓝图
    from app.routes.user import user_bp
    app.register_blueprint(user_bp, url_prefix='/user')
    
    # 工具箱蓝图
    from app.routes.tools import tools_bp
    app.register_blueprint(tools_bp, url_prefix='/tools')
    
    # 研究工具蓝图
    from app.routes.research.qualitative import qualitative_bp
    from app.routes.research.bibliometric import bibliometric_bp
    from app.routes.research.paper_query import paper_query_bp
    app.register_blueprint(qualitative_bp, url_prefix='/research/qualitative')
    app.register_blueprint(bibliometric_bp, url_prefix='/research/bibliometric')
    app.register_blueprint(paper_query_bp, url_prefix='/research/paper-query')
    
    # 班级空间蓝图
    from app.routes.class_space import class_space_bp
    app.register_blueprint(class_space_bp, url_prefix='/class')

蓝图设计原则

  1. 功能内聚:相同业务领域的功能放在同一蓝图内
  2. 关注点分离:UI路由、API路由、管理路由分别组织
  3. 可扩展性:预留子模块扩展空间
  4. 命名规范:采用清晰的命名约定

中间件生态:用户活动追踪、API日志、ASGI兼容层

构建了多层次的中间件体系来处理横切关注点:

python 复制代码
# 中间件架构设计
class MiddlewarePipeline:
    """中间件管道管理器"""
    
    def __init__(self):
        self.middlewares = []
    
    def add_middleware(self, middleware_class, priority=0):
        """添加中间件"""
        self.middlewares.append({
            'class': middleware_class,
            'priority': priority
        })
        self.middlewares.sort(key=lambda x: x['priority'])
    
    def apply_middlewares(self, app):
        """应用所有中间件"""
        for middleware_info in self.middlewares:
            middleware_class = middleware_info['class']
            app.wsgi_app = middleware_class(app.wsgi_app)

# 核心中间件实现

# 1. 用户活动追踪中间件
class UserActivityTracker:
    """用户活动追踪中间件"""
    
    def __init__(self, app):
        self.app = app
        self.logger = logging.getLogger('user.activity')
    
    def __call__(self, environ, start_response):
        request_start = time.time()
        
        # 获取用户信息
        user_id = self._get_user_id(environ)
        ip_address = self._get_client_ip(environ)
        user_agent = environ.get('HTTP_USER_AGENT', '')
        
        def custom_start_response(status, headers, exc_info=None):
            # 记录请求完成时间
            request_end = time.time()
            duration = (request_end - request_start) * 1000  # 转换为毫秒
            
            # 记录用户活动
            self._log_user_activity(
                user_id=user_id,
                ip_address=ip_address,
                user_agent=user_agent,
                path=environ.get('PATH_INFO', ''),
                method=environ.get('REQUEST_METHOD', ''),
                status=status,
                duration=duration
            )
            
            return start_response(status, headers, exc_info)
        
        return self.app(environ, custom_start_response)
    
    def _get_user_id(self, environ):
        """从session中获取用户ID"""
        # 实现session解析逻辑
        pass
    
    def _get_client_ip(self, environ):
        """获取客户端真实IP"""
        # 处理各种代理情况
        forwarded_for = environ.get('HTTP_X_FORWARDED_FOR')
        if forwarded_for:
            return forwarded_for.split(',')[0].strip()
        return environ.get('REMOTE_ADDR', 'unknown')
    
    def _log_user_activity(self, **kwargs):
        """记录用户活动到数据库"""
        try:
            from app.models.user import UserActivityLog
            from app.core.extensions import db
            
            activity_log = UserActivityLog(**kwargs)
            db.session.add(activity_log)
            db.session.commit()
        except Exception as e:
            self.logger.error(f"记录用户活动失败: {e}")

# 2. API日志中间件
class APILoggerMiddleware:
    """API请求日志中间件"""
    
    def __init__(self, app):
        self.app = app
        self.logger = logging.getLogger('api.requests')
        self.excluded_paths = {
            '/health',
            '/metrics',
            '/static/',
            '/favicon.ico'
        }
    
    def __call__(self, environ, start_response):
        path = environ.get('PATH_INFO', '')
        
        # 过滤不需要记录的路径
        if any(path.startswith(excluded) for excluded in self.excluded_paths):
            return self.app(environ, start_response)
        
        request_start = time.time()
        request_id = str(uuid.uuid4())[:8]
        
        # 记录请求开始
        self.logger.info(
            f"[{request_id}] {environ.get('REQUEST_METHOD')} {path} "
            f"from {self._get_client_ip(environ)}"
        )
        
        def logging_start_response(status, headers, exc_info=None):
            request_end = time.time()
            duration = (request_end - request_start) * 1000
            
            # 记录响应信息
            self.logger.info(
                f"[{request_id}] Response: {status} "
                f"Duration: {duration:.2f}ms"
            )
            
            return start_response(status, headers, exc_info)
        
        return self.app(environ, logging_start_response)

# 3. ASGI兼容中间件
class ASGISessionMiddleware:
    """ASGI环境下的会话兼容中间件"""
    
    def __init__(self, app):
        self.app = app
        self.logger = logging.getLogger('asgi.middleware')
    
    async def __call__(self, scope, receive, send):
        if scope['type'] == 'http':
            await self._handle_http(scope, receive, send)
        elif scope['type'] == 'websocket':
            await self._handle_websocket(scope, receive, send)
        else:
            await self.app(scope, receive, send)
    
    async def _handle_http(self, scope, receive, send):
        """处理HTTP请求"""
        # 确保会话在ASGI环境下的正确处理
        async def wrapped_send(message):
            if message['type'] == 'http.response.start':
                # 添加必要的响应头
                headers = message.get('headers', [])
                headers.append((b'x-request-id', str(uuid.uuid4()).encode()))
                message['headers'] = headers
            await send(message)
        
        await self.app(scope, receive, wrapped_send)

# 中间件管道配置
def setup_middleware_pipeline(app):
    """配置中间件管道"""
    pipeline = MiddlewarePipeline()
    
    # 按优先级添加中间件
    pipeline.add_middleware(APILoggerMiddleware, priority=1)
    pipeline.add_middleware(UserActivityTracker, priority=2)
    pipeline.add_middleware(ASGISessionMiddleware, priority=3)
    
    pipeline.apply_middlewares(app)
    return app

安全装饰器体系:login_required到permission_required与api_verified_user_required

构建了完整的安全装饰器链条,支持Web和API两种场景:

python 复制代码
# 安全装饰器体系
from functools import wraps
from flask import flash, redirect, url_for, abort, jsonify, request
from flask_login import current_user
import logging

logger = logging.getLogger(__name__)

# 1. 基础认证装饰器
def login_required(f):
    """Web端登录_required装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated:
            flash('请先登录', 'warning')
            return redirect(url_for('auth.login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

# 2. API专用认证装饰器
def api_login_required(f):
    """API端点专用的登录装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated:
            logger.warning(f"API请求未授权: {request.path}")
            return jsonify({
                'error': '请先登录',
                'message': '您需要登录后才能访问此功能',
                'code': 'UNAUTHORIZED'
            }), 401
        return f(*args, **kwargs)
    return decorated_function

# 3. 身份验证装饰器
def verified_user_required(f):
    """要求用户通过身份验证"""
    @wraps(f)
    @login_required
    def decorated_function(*args, **kwargs):
        if not current_user.is_verified:
            flash('该操作需要经过身份验证', 'warning')
            return redirect(url_for('auth.verify'))
        return f(*args, **kwargs)
    return decorated_function

def api_verified_user_required(f):
    """API端点身份验证装饰器"""
    @wraps(f)
    @api_login_required
    def decorated_function(*args, **kwargs):
        if not current_user.is_verified:
            return jsonify({
                'error': '需要身份验证',
                'message': '您的账户需要完成身份验证',
                'code': 'NOT_VERIFIED'
            }), 403
        return f(*args, **kwargs)
    return decorated_function

# 4. 权限控制装饰器
def permission_required(permission):
    """基于权限的角色控制"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated:
                flash('请先登录', 'warning')
                return redirect(url_for('auth.login'))
            
            # 检查用户权限
            if not current_user.has_permission(permission):
                flash('您没有执行此操作的权限', 'danger')
                return redirect(url_for('main.index'))
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

# 5. 班级成员权限装饰器
def class_member_required(f):
    """要求用户必须是班级成员"""
    @wraps(f)
    @login_required
    def decorated_function(*args, **kwargs):
        class_id = kwargs.get('class_id')
        if not class_id:
            abort(400, description="缺少班级ID参数")
        
        from app.models.class_space import ClassSpace
        class_space = ClassSpace.query.get(class_id)
        
        if not class_space:
            flash('班级不存在', 'warning')
            return redirect(url_for('class_space.index'))
        
        if not class_space.is_member(current_user.id):
            flash('您不是本班级成员,无权访问', 'warning')
            return redirect(url_for('class_space.index'))
        
        return f(*args, **kwargs)
    return decorated_function

# 6. 班级管理员权限装饰器
def class_admin_required(f):
    """要求用户必须是班级管理员"""
    @wraps(f)
    @login_required
    @class_member_required
    def decorated_function(*args, **kwargs):
        class_id = kwargs.get('class_id')
        from app.models.class_space import ClassSpace
        class_space = ClassSpace.query.get(class_id)
        
        if not class_space.is_admin(current_user.id):
            flash('需要管理员权限才能执行此操作', 'warning')
            return redirect(url_for('class_space.detail', class_id=class_id))
        
        return f(*args, **kwargs)
    return decorated_function

# 装饰器使用示例
@main_bp.route('/dashboard')
@login_required
@verified_user_required
def dashboard():
    """用户仪表板"""
    return render_template('dashboard.html')

@api_bp.route('/projects')
@api_login_required
@api_verified_user_required
def get_projects():
    """获取用户项目列表"""
    projects = current_user.projects.all()
    return jsonify([project.to_dict() for project in projects])

@class_space_bp.route('/<int:class_id>/settings')
@class_admin_required
def class_settings(class_id):
    """班级设置页面"""
    return render_template('class/settings.html')

路由扁平化设计:URL长度减少14.3%的实践

通过路由重构实现URL结构优化:

python 复制代码
# 路由扁平化前的深层嵌套结构
# /user/profile/settings/account/password/reset/confirm/<token>
# /research/tools/bibliometric/visualization/network/analysis/export/pdf

# 路由扁平化后的优化结构
# /account/password-reset/<token>
# /bibvis/network-export/pdf

class RouteFlattener:
    """路由扁平化工具"""
    
    def __init__(self):
        self.redirects = {}  # 旧URL到新URL的映射
    
    def flatten_routes(self, app):
        """执行路由扁平化"""
        # 1. 分析现有路由结构
        route_analysis = self._analyze_route_depth(app)
        
        # 2. 生成扁平化映射
        flattened_mapping = self._generate_flattened_routes(route_analysis)
        
        # 3. 应用重定向规则
        self._apply_redirects(app, flattened_mapping)
        
        return flattened_mapping
    
    def _analyze_route_depth(self, app):
        """分析路由深度"""
        routes = []
        for rule in app.url_map.iter_rules():
            depth = rule.rule.count('/')
            routes.append({
                'endpoint': rule.endpoint,
                'rule': rule.rule,
                'depth': depth,
                'methods': list(rule.methods)
            })
        
        # 按深度排序
        routes.sort(key=lambda x: x['depth'], reverse=True)
        return routes
    
    def _generate_flattened_routes(self, route_analysis):
        """生成扁平化路由映射"""
        mapping = {}
        
        # 常见的扁平化模式
        patterns = {
            '/user/profile/': '/profile/',
            '/user/settings/': '/settings/',
            '/research/tools/': '/tools/',
            '/admin/panel/': '/admin/',
            '/api/v1/users/': '/api/users/',
            '/class/space/': '/class/',
        }
        
        for route_info in route_analysis:
            original_rule = route_info['rule']
            new_rule = original_rule
            
            # 应用扁平化模式
            for pattern, replacement in patterns.items():
                if original_rule.startswith(pattern):
                    new_rule = original_rule.replace(pattern, replacement)
                    break
            
            # 如果发生了变化,记录映射
            if new_rule != original_rule:
                mapping[original_rule] = {
                    'new_rule': new_rule,
                    'endpoint': route_info['endpoint'],
                    'methods': route_info['methods']
                }
        
        return mapping
    
    def _apply_redirects(self, app, mapping):
        """应用重定向规则"""
        for old_rule, redirect_info in mapping.items():
            # 为旧URL创建重定向端点
            @app.route(old_rule, methods=redirect_info['methods'])
            def redirect_endpoint(**kwargs):
                new_url = url_for(
                    redirect_info['endpoint'], 
                    **kwargs
                )
                return redirect(new_url, code=301)

# 实际应用示例
def optimize_url_structure(app):
    """优化URL结构"""
    flattener = RouteFlattener()
    mapping = flattener.flatten_routes(app)
    
    # 统计优化效果
    total_routes = len(mapping)
    avg_depth_reduction = sum(
        info['rule'].count('/') - info['new_rule'].count('/') 
        for info in mapping.values()
    ) / total_routes if total_routes > 0 else 0
    
    logger.info(f"URL扁平化完成:")
    logger.info(f"- 优化路由数: {total_routes}")
    logger.info(f"- 平均深度减少: {avg_depth_reduction:.1f} 层")
    logger.info(f"- URL长度减少: {avg_depth_reduction/3*100:.1f}%")  # 估算
    
    return mapping

# 路由结构对比
"""
优化前 (深度嵌套):
/user/profile/personal/information/edit
/research/tools/bibliometric/visualization/network/analysis/settings
/admin/panel/system/configuration/database/connection/pool

优化后 (扁平结构):
/profile/edit
/bibvis/network-settings
/admin/database-pool
"""

Madechango.com结构:从app/routes到250+个API端点的成长史

项目路由系统的演进历程:

python 复制代码
# 第一阶段:单文件路由 (v1.0)
# app/routes.py - 所有路由都在一个文件中
def register_routes(app):
    @app.route('/')
    def index():
        pass
    
    @app.route('/login')
    def login():
        pass
    
    # ... 50+个路由函数

# 第二阶段:蓝图初步拆分 (v2.0)
# app/routes/__init__.py
def register_blueprints(app):
    from .auth import auth_bp
    from .user import user_bp
    from .main import main_bp
    
    app.register_blueprint(auth_bp, url_prefix='/auth')
    app.register_blueprint(user_bp, url_prefix='/user')
    app.register_blueprint(main_bp)

# 第三阶段:细粒度模块化 (v3.0)
# app/routes/research/__init__.py
def register_research_blueprints(app):
    from .qualitative import qualitative_bp
    from .bibliometric import bibliometric_bp
    from .paper_query import paper_query_bp
    from .statistics import statistics_bp
    from .journal import journal_bp
    from .thesis_gen import thesis_gen_bp
    
    app.register_blueprint(qualitative_bp, url_prefix='/qualitative')
    app.register_blueprint(bibliometric_bp, url_prefix='/bibliometric')
    # ... 其他研究工具蓝图

# 第四阶段:API端点专业化 (当前版本)
class RouteRegistry:
    """路由注册管理器"""
    
    def __init__(self):
        self.api_endpoints = 0
        self.web_endpoints = 0
        self.blueprint_count = 0
    
    def register_all_routes(self, app):
        """注册所有路由"""
        
        # 1. 核心路由
        self._register_core_routes(app)
        
        # 2. 用户系统路由
        self._register_user_routes(app)
        
        # 3. 研究工具路由
        self._register_research_routes(app)
        
        # 4. 班级空间路由
        self._register_class_routes(app)
        
        # 5. 工具箱路由
        self._register_tools_routes(app)
        
        # 6. API路由
        self._register_api_routes(app)
        
        logger.info(f"路由注册完成:")
        logger.info(f"- 蓝图数量: {self.blueprint_count}")
        logger.info(f"- API端点: {self.api_endpoints}")
        logger.info(f"- Web端点: {self.web_endpoints}")
        logger.info(f"- 总端点数: {self.api_endpoints + self.web_endpoints}")
    
    def _register_core_routes(self, app):
        """注册核心路由"""
        from app.routes.main import main_bp
        from app.routes.auth import auth_bp
        app.register_blueprint(main_bp)
        app.register_blueprint(auth_bp, url_prefix='/auth')
        self.blueprint_count += 2
    
    def _register_research_routes(self, app):
        """注册研究工具路由"""
        blueprints = [
            ('qualitative', '/research/qualitative'),
            ('bibliometric', '/research/bibliometric'),
            ('paper_query', '/research/paper-query'),
            ('statistics', '/research/statistics'),
            ('journal', '/research/journal'),
            ('thesis_gen', '/research/thesis-gen'),
        ]
        
        for name, prefix in blueprints:
            module = __import__(f'app.routes.research.{name}', fromlist=['bp'])
            bp = getattr(module, f'{name}_bp')
            app.register_blueprint(bp, url_prefix=prefix)
            self.blueprint_count += 1
            
            # 统计端点数量
            for rule in bp.url_map.iter_rules():
                if rule.endpoint.startswith('api_'):
                    self.api_endpoints += 1
                else:
                    self.web_endpoints += 1

# 路由统计和监控
class RouteMetricsCollector:
    """路由指标收集器"""
    
    def collect_metrics(self, app):
        """收集路由相关指标"""
        metrics = {
            'total_endpoints': 0,
            'api_endpoints': 0,
            'web_endpoints': 0,
            'blueprints': 0,
            'average_depth': 0,
            'deepest_route': '',
            'most_used_methods': {}
        }
        
        # 分析所有路由规则
        for rule in app.url_map.iter_rules():
            metrics['total_endpoints'] += 1
            
            # 区分API和Web端点
            if rule.rule.startswith('/api/') or 'api_' in rule.endpoint:
                metrics['api_endpoints'] += 1
            else:
                metrics['web_endpoints'] += 1
            
            # 统计HTTP方法使用情况
            for method in rule.methods:
                if method != 'HEAD':
                    metrics['most_used_methods'][method] = \
                        metrics['most_used_methods'].get(method, 0) + 1
        
        # 统计蓝图数量
        metrics['blueprints'] = len(app.blueprints)
        
        # 计算平均深度
        depths = [rule.rule.count('/') for rule in app.url_map.iter_rules()]
        metrics['average_depth'] = sum(depths) / len(depths) if depths else 0
        
        # 找到最深的路由
        if depths:
            max_depth = max(depths)
            deepest_rules = [rule.rule for rule in app.url_map.iter_rules() 
                           if rule.rule.count('/') == max_depth]
            metrics['deepest_route'] = deepest_rules[0] if deepest_rules else ''
        
        return metrics

# 使用示例
registry = RouteRegistry()
registry.register_all_routes(app)

collector = RouteMetricsCollector()
metrics = collector.collect_metrics(app)
print(f"Madechango.com路由统计:")
print(f"- 总端点数: {metrics['total_endpoints']}")
print(f"- API端点: {metrics['api_endpoints']}")
print(f"- Web端点: {metrics['web_endpoints']}")
print(f"- 蓝图数量: {metrics['blueprints']}")
print(f"- 平均路由深度: {metrics['average_depth']:.1f}")

RESTful API规范:统一错误处理与版本控制

建立了标准化的API设计规范:

python 复制代码
# RESTful API规范实现
from flask import jsonify
from werkzeug.exceptions import HTTPException
import traceback

class APIResponse:
    """标准化API响应格式"""
    
    @staticmethod
    def success(data=None, message="操作成功", code=200):
        """成功响应"""
        return jsonify({
            'success': True,
            'data': data,
            'message': message,
            'code': code,
            'timestamp': int(time.time())
        }), code
    
    @staticmethod
    def error(message="操作失败", code=400, details=None):
        """错误响应"""
        response = {
            'success': False,
            'data': None,
            'message': message,
            'code': code,
            'timestamp': int(time.time())
        }
        
        if details:
            response['details'] = details
            
        return jsonify(response), code

# 统一错误处理
@app.errorhandler(Exception)
def handle_exception(e):
    """全局异常处理"""
    
    # HTTP异常
    if isinstance(e, HTTPException):
        return APIResponse.error(
            message=e.description,
            code=e.code
        )
    
    # 业务异常
    if hasattr(e, 'code') and hasattr(e, 'message'):
        return APIResponse.error(
            message=e.message,
            code=getattr(e, 'status_code', 400),
            details=getattr(e, 'details', None)
        )
    
    # 未知异常
    logger.error(f"未处理的异常: {str(e)}")
    logger.error(traceback.format_exc())
    
    # 生产环境不暴露详细错误信息
    if app.config.get('DEBUG', False):
        return APIResponse.error(
            message=str(e),
            code=500,
            details={'traceback': traceback.format_exc()}
        )
    else:
        return APIResponse.error(
            message="服务器内部错误",
            code=500
        )

# API版本控制装饰器
def api_version(version):
    """API版本控制装饰器"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # 检查Accept头中的版本信息
            accept_header = request.headers.get('Accept', '')
            if f'v{version}' not in accept_header and \
               f'version={version}' not in request.args:
                return APIResponse.error(
                    message=f"需要指定API版本 v{version}",
                    code=400
                )
            
            # 设置响应头
            response = f(*args, **kwargs)
            if isinstance(response, tuple):
                resp, status = response
                resp.headers['API-Version'] = f'v{version}'
                return resp, status
            else:
                response.headers['API-Version'] = f'v{version}'
                return response
        return decorated_function
    return decorator

# 分页支持
class Pagination:
    """API分页支持"""
    
    def __init__(self, page=1, per_page=20, max_per_page=100):
        self.page = max(1, int(page))
        self.per_page = min(max_per_page, max(1, int(per_page)))
        self.offset = (self.page - 1) * self.per_page
    
    def paginate_query(self, query):
        """对查询进行分页"""
        total = query.count()
        items = query.offset(self.offset).limit(self.per_page).all()
        
        return {
            'items': [item.to_dict() for item in items],
            'pagination': {
                'page': self.page,
                'per_page': self.per_page,
                'total': total,
                'pages': (total + self.per_page - 1) // self.per_page,
                'has_next': self.page * self.per_page < total,
                'has_prev': self.page > 1
            }
        }

# API路由示例
@api_bp.route('/users', methods=['GET'])
@api_version(1)
@api_login_required
def get_users():
    """获取用户列表"""
    try:
        # 参数验证
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 20, type=int)
        
        # 构建查询
        query = User.query.filter(User.is_active == True)
        
        # 应用分页
        pagination = Pagination(page, per_page)
        result = pagination.paginate_query(query)
        
        return APIResponse.success(
            data=result['items'],
            message="用户列表获取成功"
        )
        
    except ValueError as e:
        return APIResponse.error(
            message="参数格式错误",
            code=400,
            details={"error": str(e)}
        )
    except Exception as e:
        logger.error(f"获取用户列表失败: {e}")
        return APIResponse.error(
            message="获取用户列表失败",
            code=500
        )

@api_bp.route('/users/<int:user_id>', methods=['GET'])
@api_version(1)
@api_login_required
def get_user(user_id):
    """获取单个用户信息"""
    user = User.query.get(user_id)
    if not user:
        return APIResponse.error(
            message="用户不存在",
            code=404
        )
    
    return APIResponse.success(
        data=user.to_dict(),
        message="用户信息获取成功"
    )

通过这套完整的蓝图系统和中间件架构,Madechango.com实现了高度模块化、可维护性强的路由管理体系,为项目的持续发展提供了坚实的基础架构支撑。

项目原型:https://madechango.com

相关推荐
YJlio5 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
l1t5 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
山塘小鱼儿7 小时前
本地Ollama+Agent+LangGraph+LangSmith运行
python·langchain·ollama·langgraph·langsimth
码说AI7 小时前
python快速绘制走势图对比曲线
开发语言·python
wait_luky7 小时前
python作业3
开发语言·python
Python大数据分析@8 小时前
tkinter可以做出多复杂的界面?
python·microsoft
大黄说说8 小时前
新手选语言不再纠结:Java、Python、Go、JavaScript 四大热门语言全景对比与学习路线建议
java·python·golang
小小张说故事9 小时前
SQLAlchemy 技术入门指南
后端·python
我是章汕呐9 小时前
拆解Libvio.link爬虫:从动态页面到反爬对抗的实战解析
爬虫·python