03-Flask请求上下文响应与错误处理机制深度解析

Flask请求上下文、响应与错误处理机制深度解析

本文基于Flask 3.0+版本编写,深入剖析Flask请求处理的核心机制,包括请求上下文管理、响应构建、错误处理及中间件模式,帮助开发者构建健壮的Web应用。


一、请求处理机制概述

1.1 Flask请求处理架构

Flask的请求处理机制基于WSGI规范,通过上下文管理器实现请求隔离和状态管理。
上下文栈
请求处理流程
HTTP请求
WSGI Server
Flask.call
创建RequestContext
创建ApplicationContext
执行before_request
路由匹配
执行视图函数
执行after_request
执行teardown_request
销毁ApplicationContext
销毁RequestContext
返回Response
_app_ctx_stack
_request_ctx_stack

1.2 核心组件关系

contains
references
produces
RequestContext
+Request request
+AppContext app_context
+dict session
+push()
+pop()
+copy()
AppContext
+Flask app
+dict url_adapter
+dict g
+push()
+pop()
Request
+str method
+str path
+dict args
+dict form
+dict json
+dict headers
+dict cookies
+dict files
+str environ
Response
+str data
+int status_code
+dict headers
+set_cookie()
+delete_cookie()


二、请求对象详解

2.1 Request对象结构

Flask的request对象是werkzeug.wrappers.Request的子类,封装了HTTP请求的所有信息。

python 复制代码
"""
Flask Request对象详解

request是一个代理对象,指向当前请求上下文中的Request实例
"""

from flask import Flask, request

app = Flask(__name__)


@app.route('/request-info', methods=['GET', 'POST'])
def request_info():
    """
    Request对象属性完整示例
    """
    # ============================================
    # 请求行信息
    # ============================================
    method = request.method              # HTTP方法: 'GET', 'POST', etc.
    url = request.url                    # 完整URL: 'http://localhost:5000/path?a=1'
    base_url = request.base_url          # 不含查询参数的URL
    url_root = request.url_root          # 应用根URL
    path = request.path                  # 路径部分: '/path'
    full_path = request.full_path        # 路径+查询参数: '/path?a=1'
    script_root = request.script_root    # 脚本根路径
    host = request.host                  # 主机名: 'localhost:5000'
    host_url = request.host_url          # 主机URL: 'http://localhost:5000/'
    
    # ============================================
    # 请求参数
    # ============================================
    args = request.args                  # URL查询参数
    form = request.form                  # 表单数据
    values = request.values              # args + form 合并
    json = request.json                  # JSON数据(自动解析)
    data = request.data                  # 原始请求体
    files = request.files                # 上传文件
    
    # ============================================
    # 请求头
    # ============================================
    headers = request.headers            # 请求头字典
    content_type = request.content_type  # Content-Type头
    content_length = request.content_length  # Content-Length
    mimetype = request.mimetype          # MIME类型
    mimetype_params = request.mimetype_params  # MIME参数
    
    # ============================================
    # Cookie
    # ============================================
    cookies = request.cookies            # Cookie字典
    
    # ============================================
    # 客户端信息
    # ============================================
    remote_addr = request.remote_addr    # 客户端IP
    user_agent = request.user_agent      # User-Agent对象
    referrer = request.referrer          # 来源页面
    origin = request.origin              # Origin头
    
    # ============================================
    # 其他属性
    # ============================================
    environ = request.environ            # WSGI环境字典
    is_json = request.is_json            # 是否为JSON请求
    is_secure = request.is_secure        # 是否为HTTPS
    is_xhr = request.is_xhr              # 是否为AJAX请求(已弃用)
    blueprint = request.blueprint        # 当前蓝图名
    endpoint = request.endpoint          # 当前端点名
    view_args = request.view_args        # URL参数字典
    routing_exception = request.routing_exception  # 路由异常
    
    return {
        'method': method,
        'path': path,
        'args': dict(args),
        'is_json': is_json,
        'remote_addr': remote_addr
    }

2.2 请求参数获取详解

python 复制代码
"""
请求参数获取详解
"""

from flask import Flask, request, jsonify

app = Flask(__name__)


# ============================================
# URL查询参数 (request.args)
# ============================================
@app.route('/search')
def search():
    """
    URL查询参数示例
    URL: /search?q=flask&page=2&sort=date
    
    request.args 是 ImmutableMultiDict 类型
    """
    # 获取单个值
    query = request.args.get('q')              # 'flask'
    query = request.args.get('q', default='')  # 带默认值
    query = request.args['q']                  # 不存在会抛出KeyError
    
    # 获取多个同名参数
    tags = request.args.getlist('tag')         # ['python', 'web']
    
    # 转换类型
    page = request.args.get('page', default=1, type=int)
    price = request.args.get('price', type=float)
    debug = request.args.get('debug', type=bool, default=False)
    
    # 获取所有参数
    all_args = dict(request.args)
    
    return jsonify({
        'query': query,
        'page': page,
        'tags': tags,
        'all_args': all_args
    })


# ============================================
# 表单数据 (request.form)
# ============================================
@app.route('/login', methods=['POST'])
def login():
    """
    表单数据示例
    Content-Type: application/x-www-form-urlencoded
    
    request.form 是 ImmutableMultiDict 类型
    """
    username = request.form.get('username')
    password = request.form.get('password')
    remember = request.form.get('remember', type=bool, default=False)
    
    # 获取所有表单字段
    all_form = dict(request.form)
    
    return jsonify({
        'username': username,
        'remember': remember
    })


# ============================================
# JSON数据 (request.json)
# ============================================
@app.route('/api/users', methods=['POST'])
def create_user():
    """
    JSON数据示例
    Content-Type: application/json
    
    request.json 自动解析JSON请求体
    """
    # 方式1: 使用 request.json
    data = request.json
    
    # 方式2: 使用 request.get_json()
    data = request.get_json()
    data = request.get_json(force=True)    # 强制解析(忽略Content-Type)
    data = request.get_json(silent=True)   # 解析失败返回None
    data = request.get_json(cache=True)    # 缓存解析结果
    
    if data is None:
        return jsonify({'error': 'Invalid JSON'}), 400
    
    username = data.get('username')
    email = data.get('email')
    
    return jsonify({
        'username': username,
        'email': email
    }), 201


# ============================================
# 合并参数 (request.values)
# ============================================
@app.route('/filter')
def filter_items():
    """
    request.values 合并了 args 和 form
    优先级: form > args
    """
    # 可以同时获取URL参数和表单数据
    category = request.values.get('category')
    page = request.values.get('page', default=1, type=int)
    
    return jsonify({
        'category': category,
        'page': page
    })


# ============================================
# 原始请求体 (request.data)
# ============================================
@app.route('/raw', methods=['POST'])
def raw_data():
    """
    原始请求体数据
    
    当Content-Type不是表单或JSON时使用
    """
    # 原始字节
    raw_bytes = request.data
    
    # 获取字符串
    raw_text = request.data.decode('utf-8')
    
    # 获取流
    stream = request.stream
    stream_data = stream.read()
    
    return jsonify({
        'length': len(raw_bytes),
        'content_type': request.content_type
    })


# ============================================
# 文件上传 (request.files)
# ============================================
@app.route('/upload', methods=['POST'])
def upload_file():
    """
    文件上传示例
    Content-Type: multipart/form-data
    
    request.files 是 MultiDict 类型
    """
    # 获取单个文件
    file = request.files.get('file')
    
    if file is None:
        return jsonify({'error': 'No file uploaded'}), 400
    
    # 文件属性
    filename = file.filename          # 原始文件名
    content_type = file.content_type  # 文件MIME类型
    content_length = file.content_length  # 文件大小
    
    # 读取文件内容
    content = file.read()
    
    # 保存文件
    # file.save('/path/to/save/' + filename)
    
    # 安全文件名处理
    from werkzeug.utils import secure_filename
    safe_name = secure_filename(filename)
    
    # 获取多个文件
    files = request.files.getlist('files')
    
    return jsonify({
        'filename': filename,
        'safe_name': safe_name,
        'content_type': content_type,
        'size': len(content)
    })

2.3 请求头操作

python 复制代码
"""
请求头操作详解
"""

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/headers')
def headers_info():
    """
    请求头信息
    
    request.headers 是 EnvironHeaders 类型
    类似字典,但键不区分大小写
    """
    # 获取单个请求头
    content_type = request.headers.get('Content-Type')
    user_agent = request.headers.get('User-Agent')
    authorization = request.headers.get('Authorization')
    
    # 不区分大小写
    content_type = request.headers.get('content-type')  # 同上
    
    # 获取所有请求头
    all_headers = dict(request.headers)
    
    # 常用请求头快捷属性
    host = request.host
    origin = request.origin
    referrer = request.referrer
    
    # 判断请求头是否存在
    has_auth = 'Authorization' in request.headers
    
    return jsonify({
        'content_type': content_type,
        'user_agent': str(user_agent),
        'has_auth': has_auth,
        'all_headers': all_headers
    })


@app.route('/auth-check')
def auth_check():
    """
    Authorization头解析
    """
    auth_header = request.headers.get('Authorization')
    
    if auth_header is None:
        return jsonify({'error': 'No authorization header'}), 401
    
    # Bearer Token
    if auth_header.startswith('Bearer '):
        token = auth_header[7:]
        return jsonify({'token': token})
    
    # Basic Auth
    if auth_header.startswith('Basic '):
        import base64
        credentials = base64.b64decode(auth_header[6:]).decode('utf-8')
        username, password = credentials.split(':', 1)
        return jsonify({'username': username})
    
    return jsonify({'error': 'Invalid authorization header'}), 401

2.4 Cookie操作

python 复制代码
"""
Cookie操作详解
"""

from flask import Flask, request, make_response

app = Flask(__name__)


@app.route('/get-cookies')
def get_cookies():
    """
    获取Cookie
    
    request.cookies 是字典类型
    """
    # 获取单个Cookie
    session_id = request.cookies.get('session_id')
    user_pref = request.cookies.get('theme', default='light')
    
    # 获取所有Cookie
    all_cookies = dict(request.cookies)
    
    return {
        'session_id': session_id,
        'theme': user_pref,
        'all_cookies': all_cookies
    }


@app.route('/set-cookies')
def set_cookies():
    """
    设置Cookie
    
    通过Response对象设置
    """
    resp = make_response('Cookies set')
    
    # 基本设置
    resp.set_cookie('username', 'john')
    
    # 完整参数
    resp.set_cookie(
        key='session_id',           # Cookie名称
        value='abc123',             # Cookie值
        max_age=3600,               # 最大存活时间(秒)
        expires=None,               # 过期时间
        path='/',                   # 有效路径
        domain=None,                # 有效域名
        secure=True,                # 仅HTTPS传输
        httponly=True,              # 禁止JS访问
        samesite='Lax'              # 同站策略: None, Lax, Strict
    )
    
    # 删除Cookie
    resp.delete_cookie('old_cookie')
    
    return resp

三、请求钩子

3.1 钩子函数详解

python 复制代码
"""
Flask请求钩子详解

请求钩子允许在请求处理的不同阶段执行自定义代码
"""

from flask import Flask, request, g, jsonify
import time
import functools

app = Flask(__name__)


# ============================================
# before_request: 请求前执行
# ============================================
@app.before_request
def before_request_handler():
    """
    每个请求前执行
    
    返回值:
        - None: 继续处理请求
        - Response: 中断请求,直接返回响应
    
    常用场景:
        - 请求日志记录
        - 用户认证检查
        - 请求计时
        - 数据库连接初始化
    """
    # 请求计时
    g.start_time = time.time()
    
    # 请求日志
    app.logger.info(
        f'Request: {request.method} {request.path} '
        f'from {request.remote_addr}'
    )
    
    # 示例: API请求认证检查
    if request.path.startswith('/api/'):
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'error': 'Unauthorized'}), 401


# ============================================
# before_first_request: 首次请求前执行(已弃用)
# ============================================
# Flask 2.3+ 已弃用,建议使用其他方式初始化
# 如应用工厂模式中的初始化代码


# ============================================
# after_request: 请求后执行
# ============================================
@app.after_request
def after_request_handler(response):
    """
    每个请求后执行(视图函数返回响应后)
    
    参数:
        response: Response对象
    
    返回值:
        必须返回Response对象
    
    特点:
        - 即使视图函数抛出异常,也会执行
        - 可以修改响应对象
    
    常用场景:
        - 添加响应头
        - 响应日志记录
        - 性能监控
    """
    # 计算请求耗时
    if hasattr(g, 'start_time'):
        elapsed = time.time() - g.start_time
        response.headers['X-Response-Time'] = f'{elapsed:.3f}s'
    
    # 添加安全响应头
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    
    # 响应日志
    app.logger.info(
        f'Response: {response.status_code} '
        f'for {request.method} {request.path}'
    )
    
    return response


# ============================================
# teardown_request: 请求销毁时执行
# ============================================
@app.teardown_request
def teardown_request_handler(exception=None):
    """
    请求上下文销毁时执行
    
    参数:
        exception: 异常对象(如果有异常)
    
    特点:
        - 无论是否发生异常都会执行
        - 在after_request之后执行
        - 不能返回值
    
    常用场景:
        - 数据库会话关闭
        - 资源清理
        - 错误日志记录
    """
    # 数据库会话清理
    # db.session.remove()
    
    # 异常日志
    if exception:
        app.logger.error(f'Request error: {exception}')


# ============================================
# teardown_appcontext: 应用上下文销毁时执行
# ============================================
@app.teardown_appcontext
def teardown_appcontext_handler(exception=None):
    """
    应用上下文销毁时执行
    
    在teardown_request之后执行
    通常用于清理应用级别的资源
    """
    # 清理数据库连接池
    pass


# ============================================
# 钩子执行顺序
# ============================================
"""
请求钩子执行顺序:

正常请求:
1. before_request
2. 视图函数
3. after_request
4. teardown_request
5. teardown_appcontext

异常请求:
1. before_request
2. 视图函数(抛出异常)
3. after_request(不执行)
4. teardown_request(执行,exception参数有值)
5. teardown_appcontext
"""

3.2 钩子函数应用示例

python 复制代码
"""
请求钩子实际应用示例
"""

from flask import Flask, request, g, jsonify
import time
import logging
from functools import wraps

app = Flask(__name__)


# ============================================
# 示例1: 请求性能监控
# ============================================
@app.before_request
def start_timer():
    """开始计时"""
    g.start_time = time.time()


@app.after_request
def end_timer(response):
    """结束计时并记录"""
    if hasattr(g, 'start_time'):
        elapsed = time.time() - g.start_time
        
        # 添加响应头
        response.headers['X-Response-Time'] = f'{elapsed:.4f}s'
        
        # 慢请求告警
        if elapsed > 1.0:
            app.logger.warning(
                f'Slow request: {request.method} {request.path} '
                f'took {elapsed:.4f}s'
            )
    
    return response


# ============================================
# 示例2: 请求日志中间件
# ============================================
import json

@app.before_request
def log_request():
    """记录请求信息"""
    g.request_id = request.headers.get('X-Request-ID', 'N/A')
    
    log_data = {
        'request_id': g.request_id,
        'method': request.method,
        'path': request.path,
        'args': dict(request.args),
        'remote_addr': request.remote_addr,
        'user_agent': str(request.user_agent),
    }
    
    app.logger.info(f'Request: {json.dumps(log_data)}')


@app.after_request
def log_response(response):
    """记录响应信息"""
    log_data = {
        'request_id': getattr(g, 'request_id', 'N/A'),
        'status_code': response.status_code,
        'content_length': response.content_length,
    }
    
    app.logger.info(f'Response: {json.dumps(log_data)}')
    
    return response


# ============================================
# 示例3: 数据库会话管理
# ============================================
@app.before_request
def init_db_session():
    """初始化数据库会话"""
    # g.db_session = db.create_session()
    pass


@app.teardown_request
def close_db_session(exception=None):
    """关闭数据库会话"""
    session = getattr(g, 'db_session', None)
    if session is not None:
        if exception is not None:
            session.rollback()
        session.close()


# ============================================
# 示例4: API认证装饰器与钩子结合
# ============================================
def api_auth_required(f):
    """API认证装饰器"""
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        
        if not token or not token.startswith('Bearer '):
            return jsonify({'error': 'Invalid token'}), 401
        
        token = token[7:]  # Remove 'Bearer ' prefix
        
        # 验证token
        user = verify_token(token)
        if user is None:
            return jsonify({'error': 'Invalid or expired token'}), 401
        
        g.current_user = user
        return f(*args, **kwargs)
    
    return decorated


def verify_token(token):
    """验证Token(示例)"""
    # 实际项目中使用JWT或其他方式验证
    if token == 'valid-token':
        return {'id': 1, 'username': 'admin'}
    return None


@app.route('/api/protected')
@api_auth_required
def protected_api():
    """受保护的API"""
    return jsonify({
        'message': 'Protected content',
        'user': g.current_user
    })


# ============================================
# 示例5: 蓝图级钩子
# ============================================
from flask import Blueprint

api_bp = Blueprint('api', __name__, url_prefix='/api')


@api_bp.before_request
def api_before_request():
    """蓝图级before_request"""
    # 仅对/api/*路由生效
    if request.method != 'OPTIONS':
        # CORS预检请求不需要认证
        pass


@api_bp.after_request
def api_after_request(response):
    """蓝图级after_request"""
    # 添加CORS头
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    return response


app.register_blueprint(api_bp)

四、响应对象详解

4.1 Response对象构建

python 复制代码
"""
Flask响应对象详解

视图函数可以返回多种类型的值,Flask会自动转换为Response对象
"""

from flask import Flask, make_response, Response, jsonify, redirect

app = Flask(__name__)


# ============================================
# 返回值类型
# ============================================

@app.route('/string')
def return_string():
    """
    返回字符串
    
    自动转换为:
        - Response对象
        - status_code: 200
        - Content-Type: text/html
    """
    return 'Hello World'


@app.route('/tuple-string')
def return_tuple_string():
    """
    返回元组: (body, status)
    """
    return 'Created', 201


@app.route('/tuple-headers')
def return_tuple_headers():
    """
    返回元组: (body, headers)
    """
    return 'OK', {'X-Custom-Header': 'value'}


@app.route('/tuple-full')
def return_tuple_full():
    """
    返回元组: (body, status, headers)
    """
    return 'Created', 201, {'Content-Type': 'application/json'}


@app.route('/response-object')
def return_response():
    """
    返回Response对象
    """
    resp = Response('Hello World')
    resp.status_code = 200
    resp.headers['X-Custom'] = 'value'
    return resp


# ============================================
# make_response() 函数
# ============================================

@app.route('/make-response')
def use_make_response():
    """
    make_response() 创建响应对象
    
    参数可以是:
        - 字符串
        - 元组
        - Response对象
    """
    # 从字符串创建
    resp = make_response('Hello World')
    
    # 从元组创建
    resp = make_response(('Created', 201))
    
    # 设置属性
    resp.status_code = 200
    resp.headers['X-Custom-Header'] = 'value'
    resp.mimetype = 'application/json'
    
    # 设置Cookie
    resp.set_cookie('username', 'john', max_age=3600)
    
    return resp


# ============================================
# Response对象属性和方法
# ============================================

@app.route('/response-details')
def response_details():
    """Response对象详细示例"""
    resp = make_response('Response Body')
    
    # 响应体
    resp.data = 'New Body'                    # 设置响应体(字节或字符串)
    body = resp.get_data(as_text=True)        # 获取响应体(字符串)
    body_bytes = resp.get_data()              # 获取响应体(字节)
    
    # 状态
    resp.status_code = 200                    # 状态码
    resp.status = '200 OK'                    # 状态字符串
    
    # 响应头
    resp.headers['X-Custom'] = 'value'        # 设置响应头
    content_type = resp.headers.get('Content-Type')
    
    # MIME类型
    resp.mimetype = 'application/json'        # MIME类型
    resp.content_type = 'application/json; charset=utf-8'  # 完整Content-Type
    
    # 内容长度
    length = resp.content_length              # 获取内容长度
    
    # Cookie操作
    resp.set_cookie(
        key='session_id',
        value='abc123',
        max_age=3600,
        expires=None,
        path='/',
        domain=None,
        secure=True,
        httponly=True,
        samesite='Lax'
    )
    resp.delete_cookie('old_cookie')
    
    # ETag
    resp.set_etag('unique-etag')
    
    # 缓存控制
    resp.cache_control.max_age = 3600
    resp.cache_control.no_cache = True
    resp.cache_control.public = True
    
    # 最后修改时间
    from datetime import datetime
    resp.last_modified = datetime.utcnow()
    
    return resp

4.2 特殊响应类型

python 复制代码
"""
特殊响应类型详解
"""

from flask import (
    Flask, redirect, jsonify, send_file, 
    send_from_directory, make_response, Response
)
import io
import os
from datetime import datetime

app = Flask(__name__)


# ============================================
# redirect: 重定向
# ============================================

@app.route('/old-url')
def old_url():
    """
    重定向到新URL
    
    参数:
        location: 目标URL
        code: 状态码(默认302)
    """
    # 基本重定向(302临时重定向)
    return redirect('/new-url')

@app.route('/old-url-permanent')
def old_url_permanent():
    """永久重定向(301)"""
    return redirect('/new-url', code=301)

@app.route('/old-url-307')
def old_url_307():
    """
    307重定向:保留请求方法和请求体
    
    适用场景:
        - POST请求重定向
        - 需要保留请求体
    """
    return redirect('/new-url', code=307)

@app.route('/old-url-308')
def old_url_308():
    """308永久重定向:保留请求方法"""
    return redirect('/new-url', code=308)

@app.route('/new-url')
def new_url():
    return 'This is the new URL'


# 重定向状态码对比
"""
状态码对比:
- 301: 永久重定向,浏览器缓存,POST变GET
- 302: 临时重定向(默认),POST变GET
- 303: 临时重定向,强制GET
- 307: 临时重定向,保留请求方法和请求体
- 308: 永久重定向,保留请求方法和请求体
"""


# ============================================
# jsonify: JSON响应
# ============================================

@app.route('/api/user')
def api_user():
    """
    JSON响应
    
    自动设置:
        - Content-Type: application/json
        - 将Python对象序列化为JSON
    """
    user = {
        'id': 1,
        'username': 'john',
        'email': 'john@example.com',
        'created_at': datetime.utcnow().isoformat()
    }
    return jsonify(user)


@app.route('/api/users')
def api_users():
    """返回列表"""
    users = [
        {'id': 1, 'name': 'John'},
        {'id': 2, 'name': 'Jane'},
    ]
    return jsonify(users)


@app.route('/api/status')
def api_status():
    """带状态码的JSON响应"""
    return jsonify({'status': 'created'}), 201


@app.route('/api/error')
def api_error():
    """错误响应"""
    return jsonify({'error': 'Not found'}), 404


# ============================================
# send_file: 文件响应
# ============================================

@app.route('/download/<filename>')
def download_file(filename):
    """
    文件下载
    
    参数:
        path_or_file: 文件路径或文件对象
        mimetype: MIME类型
        as_attachment: 是否作为附件下载
        download_name: 下载文件名
        conditional: 是否支持条件请求
        etag: ETag生成策略
        last_modified: 最后修改时间
        max_age: 缓存时间
    """
    # 基本用法
    return send_file(
        f'/path/to/files/{filename}',
        as_attachment=True
    )


@app.route('/download/configured')
def download_configured():
    """完整参数示例"""
    return send_file(
        '/path/to/file.pdf',
        mimetype='application/pdf',
        as_attachment=True,
        download_name='document.pdf',
        conditional=True,
        etag=True,
        max_age=3600
    )


@app.route('/download/memory')
def download_from_memory():
    """从内存下载"""
    # 创建内存文件
    data = b'Hello, this is file content from memory.'
    file_obj = io.BytesIO(data)
    
    return send_file(
        file_obj,
        mimetype='text/plain',
        as_attachment=True,
        download_name='memory-file.txt'
    )


@app.route('/download/generated')
def download_generated():
    """生成CSV下载"""
    import csv
    
    # 创建内存文件
    output = io.StringIO()
    writer = csv.writer(output)
    writer.writerow(['ID', 'Name', 'Email'])
    writer.writerow([1, 'John', 'john@example.com'])
    writer.writerow([2, 'Jane', 'jane@example.com'])
    
    output.seek(0)
    
    return send_file(
        io.BytesIO(output.getvalue().encode('utf-8')),
        mimetype='text/csv',
        as_attachment=True,
        download_name='users.csv'
    )


# ============================================
# send_from_directory: 安全文件下载
# ============================================

@app.route('/files/<path:filename>')
def download_from_directory(filename):
    """
    从目录安全下载文件
    
    参数:
        directory: 基础目录(绝对路径)
        path: 相对路径
        **kwargs: 其他参数传递给send_file
    
    安全特性:
        - 防止目录遍历攻击
        - 自动处理路径规范化
    """
    upload_dir = '/path/to/uploads'
    
    return send_from_directory(
        upload_dir,
        filename,
        as_attachment=True
    )


@app.route('/images/<filename>')
def serve_image(filename):
    """服务图片文件"""
    image_dir = os.path.join(app.root_path, 'static', 'images')
    
    return send_from_directory(
        image_dir,
        filename,
        mimetype='image/jpeg'
    )


# ============================================
# 流式响应
# ============================================

@app.route('/stream')
def stream_response():
    """
    流式响应
    
    适用场景:
        - 大文件下载
        - 实时数据推送
        - SSE (Server-Sent Events)
    """
    def generate():
        for i in range(100):
            yield f'data: {i}\n\n'
            import time
            time.sleep(0.1)
    
    return Response(
        generate(),
        mimetype='text/event-stream'
    )


@app.route('/stream/csv')
def stream_csv():
    """流式CSV导出"""
    import csv
    
    def generate():
        output = io.StringIO()
        writer = csv.writer(output)
        
        # 写入表头
        writer.writerow(['ID', 'Name', 'Email'])
        yield output.getvalue()
        output.seek(0)
        output.truncate(0)
        
        # 写入数据
        for i in range(1000):
            writer.writerow([i, f'User {i}', f'user{i}@example.com'])
            yield output.getvalue()
            output.seek(0)
            output.truncate(0)
    
    return Response(
        generate(),
        mimetype='text/csv',
        headers={
            'Content-Disposition': 'attachment; filename=users.csv'
        }
    )


@app.route('/stream/large-file')
def stream_large_file():
    """大文件流式下载"""
    def generate():
        chunk_size = 8192  # 8KB chunks
        with open('/path/to/large/file.bin', 'rb') as f:
            while True:
                chunk = f.read(chunk_size)
                if not chunk:
                    break
                yield chunk
    
    return Response(
        generate(),
        mimetype='application/octet-stream',
        headers={
            'Content-Disposition': 'attachment; filename=large-file.bin'
        }
    )

4.3 Cookie完整操作

python 复制代码
"""
Cookie完整操作详解
"""

from flask import Flask, make_response, request

app = Flask(__name__)


@app.route('/cookie/set')
def set_cookie():
    """
    设置Cookie完整示例
    """
    resp = make_response('Cookie set')
    
    # 基本设置
    resp.set_cookie('simple', 'value')
    
    # 完整参数
    resp.set_cookie(
        key='session_id',           # Cookie名称
        value='abc123def456',       # Cookie值
        max_age=3600,               # 最大存活时间(秒)
        # 或使用expires指定过期时间
        # expires=datetime(2024, 12, 31, 23, 59, 59),
        path='/',                   # 有效路径
        domain='.example.com',      # 有效域名(带点表示包含子域名)
        secure=True,                # 仅HTTPS传输
        httponly=True,              # 禁止JavaScript访问
        samesite='Lax'              # 同站策略
    )
    
    # SameSite取值说明:
    # - None: 允许跨站发送(需要Secure=True)
    # - Lax: 允许顶级导航和GET表单跨站发送
    # - Strict: 完全禁止跨站发送
    
    return resp


@app.route('/cookie/get')
def get_cookie():
    """获取Cookie"""
    # 获取单个Cookie
    session_id = request.cookies.get('session_id')
    
    # 带默认值
    theme = request.cookies.get('theme', default='light')
    
    # 获取所有Cookie
    all_cookies = dict(request.cookies)
    
    # 检查Cookie是否存在
    has_session = 'session_id' in request.cookies
    
    return {
        'session_id': session_id,
        'theme': theme,
        'has_session': has_session
    }


@app.route('/cookie/delete')
def delete_cookie():
    """删除Cookie"""
    resp = make_response('Cookie deleted')
    
    # 删除单个Cookie
    resp.delete_cookie('session_id')
    
    # 删除时需要匹配path和domain
    resp.delete_cookie(
        key='session_id',
        path='/',
        domain='.example.com'
    )
    
    return resp


@app.route('/cookie/update')
def update_cookie():
    """更新Cookie(重新设置即可)"""
    resp = make_response('Cookie updated')
    
    # 重新设置即更新
    resp.set_cookie('theme', 'dark', max_age=3600)
    
    return resp

五、错误处理机制

5.1 错误处理器注册

python 复制代码
"""
Flask错误处理机制详解
"""

from flask import Flask, jsonify, render_template, request

app = Flask(__name__)


# ============================================
# HTTP错误处理器
# ============================================

@app.errorhandler(400)
def bad_request(error):
    """400 Bad Request"""
    if request.is_json:
        return jsonify({
            'error': 'Bad Request',
            'message': str(error.description)
        }), 400
    return render_template('errors/400.html', error=error), 400


@app.errorhandler(401)
def unauthorized(error):
    """401 Unauthorized"""
    if request.is_json:
        return jsonify({
            'error': 'Unauthorized',
            'message': 'Authentication required'
        }), 401
    return render_template('errors/401.html'), 401


@app.errorhandler(403)
def forbidden(error):
    """403 Forbidden"""
    if request.is_json:
        return jsonify({
            'error': 'Forbidden',
            'message': 'You do not have permission to access this resource'
        }), 403
    return render_template('errors/403.html'), 403


@app.errorhandler(404)
def not_found(error):
    """404 Not Found"""
    if request.is_json:
        return jsonify({
            'error': 'Not Found',
            'message': f'The requested URL {request.path} was not found'
        }), 404
    return render_template('errors/404.html'), 404


@app.errorhandler(405)
def method_not_allowed(error):
    """405 Method Not Allowed"""
    if request.is_json:
        return jsonify({
            'error': 'Method Not Allowed',
            'message': f'The method {request.method} is not allowed'
        }), 405
    return render_template('errors/405.html'), 405


@app.errorhandler(413)
def request_entity_too_large(error):
    """413 Request Entity Too Large"""
    if request.is_json:
        return jsonify({
            'error': 'Request Entity Too Large',
            'message': 'The uploaded file is too large'
        }), 413
    return render_template('errors/413.html'), 413


@app.errorhandler(422)
def unprocessable_entity(error):
    """422 Unprocessable Entity"""
    if request.is_json:
        return jsonify({
            'error': 'Unprocessable Entity',
            'message': 'Validation failed',
            'details': getattr(error, 'details', {})
        }), 422
    return render_template('errors/422.html'), 422


@app.errorhandler(429)
def too_many_requests(error):
    """429 Too Many Requests"""
    if request.is_json:
        return jsonify({
            'error': 'Too Many Requests',
            'message': 'Rate limit exceeded'
        }), 429
    return render_template('errors/429.html'), 429


@app.errorhandler(500)
def internal_server_error(error):
    """500 Internal Server Error"""
    # 记录错误日志
    app.logger.error(f'Internal Server Error: {error}')
    
    if request.is_json:
        return jsonify({
            'error': 'Internal Server Error',
            'message': 'An unexpected error occurred'
        }), 500
    return render_template('errors/500.html'), 500


@app.errorhandler(502)
def bad_gateway(error):
    """502 Bad Gateway"""
    if request.is_json:
        return jsonify({
            'error': 'Bad Gateway',
            'message': 'Upstream server error'
        }), 502
    return render_template('errors/502.html'), 502


@app.errorhandler(503)
def service_unavailable(error):
    """503 Service Unavailable"""
    if request.is_json:
        return jsonify({
            'error': 'Service Unavailable',
            'message': 'Service temporarily unavailable'
        }), 503
    return render_template('errors/503.html'), 503


# ============================================
# 自定义异常处理器
# ============================================

class APIError(Exception):
    """API错误基类"""
    
    def __init__(self, message, status_code=400, payload=None):
        super().__init__()
        self.message = message
        self.status_code = status_code
        self.payload = payload
    
    def to_dict(self):
        result = {
            'error': self.__class__.__name__,
            'message': self.message
        }
        if self.payload:
            result['details'] = self.payload
        return result


class ValidationError(APIError):
    """验证错误"""
    def __init__(self, message, errors=None):
        super().__init__(message, status_code=422, payload=errors)


class AuthenticationError(APIError):
    """认证错误"""
    def __init__(self, message='Authentication failed'):
        super().__init__(message, status_code=401)


class AuthorizationError(APIError):
    """授权错误"""
    def __init__(self, message='Permission denied'):
        super().__init__(message, status_code=403)


class ResourceNotFoundError(APIError):
    """资源不存在"""
    def __init__(self, resource, resource_id):
        super().__init__(
            f'{resource} with id {resource_id} not found',
            status_code=404
        )


# 注册自定义异常处理器
@app.errorhandler(APIError)
def handle_api_error(error):
    """处理API错误"""
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response


# 使用示例
@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    from myapp.models import User
    
    user = User.query.get(user_id)
    if user is None:
        raise ResourceNotFoundError('User', user_id)
    
    return jsonify(user.to_dict())


@app.route('/api/users', methods=['POST'])
def create_user():
    data = request.get_json()
    
    # 验证
    errors = validate_user_data(data)
    if errors:
        raise ValidationError('Validation failed', errors)
    
    # 创建用户
    # ...
    
    return jsonify({'id': 1}), 201


def validate_user_data(data):
    """验证用户数据"""
    errors = {}
    
    if not data.get('username'):
        errors['username'] = 'Username is required'
    elif len(data['username']) < 3:
        errors['username'] = 'Username must be at least 3 characters'
    
    if not data.get('email'):
        errors['email'] = 'Email is required'
    
    return errors

5.2 abort函数

python 复制代码
"""
abort函数详解

用于主动中断请求并返回错误响应
"""

from flask import Flask, abort, jsonify, request

app = Flask(__name__)


@app.route('/admin')
def admin():
    """权限检查示例"""
    if not is_admin():
        abort(403)  # 返回403 Forbidden
    return 'Admin Panel'


@app.route('/user/<int:user_id>')
def get_user(user_id):
    """资源检查示例"""
    user = get_user_from_db(user_id)
    if user is None:
        abort(404)  # 返回404 Not Found
    return jsonify(user)


@app.route('/api/data')
def api_data():
    """带描述的abort"""
    if not request.args.get('key'):
        abort(400, description='API key is required')
    return jsonify({'data': 'value'})


# 自定义错误响应
@app.errorhandler(400)
def bad_request(error):
    return jsonify({
        'error': 'Bad Request',
        'message': error.description
    }), 400


# abort的HTTP状态码
"""
常用abort状态码:
- 400: Bad Request - 请求格式错误
- 401: Unauthorized - 未认证
- 403: Forbidden - 无权限
- 404: Not Found - 资源不存在
- 405: Method Not Allowed - 方法不允许
- 406: Not Acceptable - 无法接受
- 409: Conflict - 资源冲突
- 410: Gone - 资源已删除
- 413: Request Entity Too Large - 请求体过大
- 415: Unsupported Media Type - 不支持的媒体类型
- 422: Unprocessable Entity - 验证失败
- 429: Too Many Requests - 请求过多
- 500: Internal Server Error - 服务器错误
- 501: Not Implemented - 未实现
- 503: Service Unavailable - 服务不可用
"""


def is_admin():
    """检查是否为管理员"""
    return False


def get_user_from_db(user_id):
    """从数据库获取用户"""
    return None

5.3 错误处理最佳实践

python 复制代码
"""
错误处理最佳实践
"""

from flask import Flask, jsonify, request, render_template
from functools import wraps
import traceback

app = Flask(__name__)


# ============================================
# 1. 统一错误响应格式
# ============================================

def error_response(message, status_code, details=None):
    """
    统一错误响应格式
    
    参数:
        message: 错误消息
        status_code: HTTP状态码
        details: 详细信息
    """
    response = {
        'success': False,
        'error': {
            'message': message,
            'status_code': status_code
        }
    }
    
    if details:
        response['error']['details'] = details
    
    if app.debug:
        response['error']['trace'] = traceback.format_exc()
    
    return jsonify(response), status_code


# ============================================
# 2. 全局异常捕获
# ============================================

@app.errorhandler(Exception)
def handle_exception(error):
    """
    捕获所有未处理的异常
    
    生产环境不应暴露详细错误信息
    """
    # 记录错误日志
    app.logger.error(
        f'Unhandled exception: {str(error)}\n'
        f'Traceback: {traceback.format_exc()}'
    )
    
    # API请求返回JSON
    if request.path.startswith('/api/'):
        return error_response(
            message='Internal server error',
            status_code=500
        )
    
    # Web请求返回错误页面
    return render_template('errors/500.html'), 500


# ============================================
# 3. 错误处理装饰器
# ============================================

def handle_errors(f):
    """
    错误处理装饰器
    
    捕获视图函数中的异常并返回统一格式
    """
    @wraps(f)
    def decorated(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except APIError as e:
            return error_response(
                message=e.message,
                status_code=e.status_code,
                details=e.payload
            )
        except Exception as e:
            app.logger.error(f'Error in {f.__name__}: {str(e)}')
            return error_response(
                message='Internal server error',
                status_code=500
            )
    return decorated


@app.route('/api/protected')
@handle_errors
def protected_route():
    """使用错误处理装饰器"""
    # 业务逻辑
    if not validate_request():
        raise ValidationError('Invalid request')
    
    return jsonify({'data': 'success'})


# ============================================
# 4. 错误日志记录
# ============================================

import logging
from logging.handlers import RotatingFileHandler
import os

def setup_error_logging(app):
    """配置错误日志"""
    if not app.debug:
        # 确保日志目录存在
        if not os.path.exists('logs'):
            os.mkdir('logs')
        
        # 文件日志
        file_handler = RotatingFileHandler(
            'logs/errors.log',
            maxBytes=10 * 1024 * 1024,  # 10MB
            backupCount=5
        )
        file_handler.setFormatter(logging.Formatter(
            '%(asctime)s %(levelname)s: %(message)s '
            '[in %(pathname)s:%(lineno)d]'
        ))
        file_handler.setLevel(logging.ERROR)
        app.logger.addHandler(file_handler)
        
        # 邮件通知(严重错误)
        from logging.handlers import SMTPHandler
        
        mail_handler = SMTPHandler(
            mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
            fromaddr=app.config['MAIL_DEFAULT_SENDER'],
            toaddrs=app.config['ADMINS'],
            subject='[MyApp] Application Error',
            credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
        )
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)


# ============================================
# 5. 错误页面模板
# ============================================

"""
templates/errors/404.html:
<!DOCTYPE html>
<html>
<head>
    <title>404 - Page Not Found</title>
</head>
<body>
    <h1>404 - Page Not Found</h1>
    <p>The page you are looking for does not exist.</p>
    <a href="{{ url_for('main.index') }}">Go to Home</a>
</body>
</html>

templates/errors/500.html:
<!DOCTYPE html>
<html>
<head>
    <title>500 - Internal Server Error</title>
</head>
<body>
    <h1>500 - Internal Server Error</h1>
    <p>Something went wrong. Please try again later.</p>
    <a href="{{ url_for('main.index') }}">Go to Home</a>
</body>
</html>
"""


def validate_request():
    return False

六、上下文管理机制

6.1 上下文栈结构

python 复制代码
"""
Flask上下文管理详解

Flask使用两个上下文栈来管理请求状态:
1. _request_ctx_stack: 请求上下文栈
2. _app_ctx_stack: 应用上下文栈
"""

from flask import Flask, request, current_app, g

app = Flask(__name__)


# ============================================
# 请求上下文
# ============================================
"""
请求上下文包含:
- request: 当前请求对象
- session: 当前会话对象

生命周期: 一个HTTP请求
"""

@app.route('/request-context')
def request_context_demo():
    """
    请求上下文示例
    """
    # request对象在请求上下文中可用
    method = request.method
    path = request.path
    
    # session对象也在请求上下文中
    # session['key'] = 'value'
    
    return {
        'method': method,
        'path': path
    }


# ============================================
# 应用上下文
# ============================================
"""
应用上下文包含:
- current_app: 当前Flask应用实例
- g: 请求期间存储数据的命名空间

生命周期: 一个请求或CLI命令
"""

@app.route('/app-context')
def app_context_demo():
    """
    应用上下文示例
    """
    # current_app指向当前Flask应用
    debug = current_app.debug
    config = current_app.config
    
    # g是请求级别的存储空间
    g.user_id = 123
    g.start_time = time.time()
    
    return {
        'debug': debug,
        'user_id': g.user_id
    }


# ============================================
# 手动创建上下文
# ============================================

import time

def background_task():
    """
    后台任务需要手动创建应用上下文
    """
    with app.app_context():
        # 现在可以访问current_app和g
        print(f'App name: {current_app.name}')
        
        # 可以访问数据库等扩展
        # from myapp.extensions import db
        # users = db.session.query(User).all()


def test_request_context():
    """
    测试请求上下文
    """
    with app.test_request_context('/test?arg=value'):
        # 现在可以访问request
        print(f'Path: {request.path}')
        print(f'Args: {request.args}')
        
        # 可以调用视图函数
        # response = app.view_functions['endpoint']()


# ============================================
# g对象详解
# ============================================

@app.before_request
def before_request():
    """
    在before_request中设置g对象
    """
    g.start_time = time.time()
    g.request_id = request.headers.get('X-Request-ID')


@app.route('/g-demo')
def g_demo():
    """
    g对象使用示例
    """
    # 获取before_request中设置的值
    start_time = g.get('start_time')
    request_id = g.get('request_id')
    
    # 设置新值
    g.processed = True
    
    return {
        'request_id': request_id,
        'elapsed': time.time() - start_time if start_time else None
    }


@app.teardown_request
def teardown_request(exception=None):
    """
    在teardown_request中清理g对象
    """
    # g对象会自动清理,无需手动操作
    pass

6.2 上下文生命周期

View AppContext RequestContext Flask WSGI Client View AppContext RequestContext Flask WSGI Client alt [AppContext not exists] alt [Last request] HTTP Request call(environ) Create RequestContext push() to _request_ctx_stack Check AppContext exists? Create AppContext push() to _app_ctx_stack before_request hooks Call view function Response after_request hooks teardown_request hooks pop() Check last request? teardown_appcontext hooks pop() Response HTTP Response

6.3 上下文代理原理

python 复制代码
"""
Flask上下文代理原理

request, current_app, g, session 都是代理对象
它们通过LocalProxy实现对栈顶对象的透明访问
"""

from werkzeug.local import LocalProxy, LocalStack

# Flask内部的实现(简化版)

# 创建栈
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()


# 创建代理
def _get_request():
    """获取当前请求对象"""
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('Working outside of request context')
    return top.request


request = LocalProxy(_get_request)


def _get_current_app():
    """获取当前应用对象"""
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('Working outside of application context')
    return top.app


current_app = LocalProxy(_get_current_app)


# 使用示例
"""
代理对象的好处:
1. 无需显式传递request对象
2. 线程安全
3. 代码简洁

# 不使用代理(需要显式传递)
def view_function(request):
    return request.method

# 使用代理(自动获取当前请求)
def view_function():
    return request.method
"""

七、WSGI中间件

7.1 中间件模式

python 复制代码
"""
WSGI中间件模式详解

中间件是包裹Flask应用的WSGI应用
可以在请求到达Flask前后进行处理
"""

from flask import Flask

app = Flask(__name__)


# ============================================
# 中间件基本结构
# ============================================

class SimpleMiddleware:
    """
    简单中间件示例
    
    WSGI中间件必须:
    1. 接受一个WSGI应用作为参数
    2. 实现__call__方法
    3. 调用被包裹的应用
    """
    
    def __init__(self, app):
        self.app = app
    
    def __call__(self, environ, start_response):
        """
        处理请求
        
        参数:
            environ: WSGI环境字典
            start_response: 开始响应的回调函数
        """
        # 请求前处理
        print(f'Before request: {environ["PATH_INFO"]}')
        
        # 调用被包裹的应用
        def custom_start_response(status, headers, exc_info=None):
            # 可以修改响应头
            headers.append(('X-Middleware', 'SimpleMiddleware'))
            return start_response(status, headers, exc_info)
        
        # 调用下一个应用
        response = self.app(environ, custom_start_response)
        
        # 响应后处理
        print(f'After response: {status}')
        
        return response


# 应用中间件
app.wsgi_app = SimpleMiddleware(app.wsgi_app)


# ============================================
# ProxyFix中间件
# ============================================

from werkzeug.middleware.proxy_fix import ProxyFix

"""
ProxyFix中间件

用于反向代理环境,修复请求头中的真实IP等信息

参数:
    x_for: X-Forwarded-For头的信任层数
    x_proto: X-Forwarded-Proto头的信任层数
    x_host: X-Forwarded-Host头的信任层数
    x_port: X-Forwarded-Port头的信任层数
    x_prefix: X-Forwarded-Prefix头的信任层数
"""

# 基本配置
app.wsgi_app = ProxyFix(
    app.wsgi_app,
    x_for=1,      # 信任一层代理
    x_proto=1,
    x_host=1,
    x_port=1
)

# 多层代理配置
app.wsgi_app = ProxyFix(
    app.wsgi_app,
    x_for=2,      # 信任两层代理
    x_proto=1,
    x_host=1
)


# ============================================
# 性能分析中间件
# ============================================

from werkzeug.middleware.profiler import ProfilerMiddleware

"""
ProfilerMiddleware

用于性能分析,生成性能报告
"""

# 启用性能分析
app.wsgi_app = ProfilerMiddleware(
    app.wsgi_app,
    profile_dir='profiles'  # 性能报告输出目录
)


# ============================================
# 自定义请求计时中间件
# ============================================

import time

class TimingMiddleware:
    """请求计时中间件"""
    
    def __init__(self, app):
        self.app = app
    
    def __call__(self, environ, start_response):
        start_time = time.time()
        
        def custom_start_response(status, headers, exc_info=None):
            elapsed = time.time() - start_time
            headers.append(('X-Response-Time', f'{elapsed:.4f}s'))
            return start_response(status, headers, exc_info)
        
        return self.app(environ, custom_start_response)


# ============================================
# 自定义错误处理中间件
# ============================================

class ErrorHandlingMiddleware:
    """全局错误处理中间件"""
    
    def __init__(self, app):
        self.app = app
    
    def __call__(self, environ, start_response):
        try:
            return self.app(environ, start_response)
        except Exception as e:
            # 记录错误
            import traceback
            error_trace = traceback.format_exc()
            print(f'Error: {error_trace}')
            
            # 返回错误响应
            status = '500 Internal Server Error'
            headers = [('Content-Type', 'application/json')]
            start_response(status, headers)
            
            import json
            return [json.dumps({
                'error': 'Internal Server Error',
                'message': str(e) if environ.get('DEBUG') else 'Server error'
            }).encode('utf-8')]


# ============================================
# 静态文件中间件
# ============================================

from whitenoise import WhiteNoise

"""
WhiteNoise中间件

用于高效服务静态文件,支持缓存和压缩
"""

# 应用WhiteNoise
app.wsgi_app = WhiteNoise(
    app.wsgi_app,
    root='static/',           # 静态文件目录
    prefix='static/',         # URL前缀
    max_age=31536000          # 缓存时间(1年)
)

# 添加额外的静态文件目录
# app.wsgi_app.add_files('media/', prefix='media/')

八、异步视图支持

8.1 异步视图基础

python 复制代码
"""
Flask异步视图支持(Flask 2.0+)

使用async def定义异步视图函数
"""

from flask import Flask, jsonify
import asyncio
import aiohttp

app = Flask(__name__)


# ============================================
# 基本异步视图
# ============================================

@app.route('/async-hello')
async def async_hello():
    """基本异步视图"""
    await asyncio.sleep(0.1)  # 模拟异步操作
    return 'Hello, Async World!'


# ============================================
# 异步HTTP请求
# ============================================

@app.route('/fetch-data')
async def fetch_data():
    """异步获取外部数据"""
    async with aiohttp.ClientSession() as session:
        async with session.get('https://api.example.com/data') as response:
            data = await response.json()
    
    return jsonify(data)


# ============================================
# 并发异步操作
# ============================================

@app.route('/parallel-fetch')
async def parallel_fetch():
    """并发获取多个数据源"""
    async def fetch_url(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.json()
    
    # 并发执行多个请求
    urls = [
        'https://api.example.com/users',
        'https://api.example.com/posts',
        'https://api.example.com/comments'
    ]
    
    results = await asyncio.gather(*[fetch_url(url) for url in urls])
    
    return jsonify({
        'users': results[0],
        'posts': results[1],
        'comments': results[2]
    })


# ============================================
# 异步数据库操作
# ============================================

@app.route('/async-users')
async def async_users():
    """
    异步数据库查询
    
    需要使用异步数据库驱动如:
    - SQLAlchemy 2.0+ async
    - asyncpg (PostgreSQL)
    - aiomysql (MySQL)
    """
    # 使用SQLAlchemy 2.0异步示例
    # from sqlalchemy.ext.asyncio import AsyncSession
    # async with AsyncSession(engine) as session:
    #     result = await session.execute(select(User))
    #     users = result.scalars().all()
    
    return jsonify({'users': []})


# ============================================
# 异步请求钩子
# ============================================

@app.before_request
async def async_before_request():
    """异步before_request钩子"""
    # 可以执行异步操作
    # await some_async_operation()
    pass


@app.after_request
async def async_after_request(response):
    """异步after_request钩子"""
    # 可以执行异步操作
    return response


# ============================================
# 异步与同步混用
# ============================================

@app.route('/mixed')
def mixed_view():
    """
    同步视图中调用异步函数
    
    使用asyncio.run()或loop
    """
    async def async_operation():
        await asyncio.sleep(0.1)
        return 'async result'
    
    # 在同步上下文中运行异步函数
    result = asyncio.run(async_operation())
    
    return result

九、最佳实践总结

python 复制代码
"""
Flask请求处理最佳实践
"""

# ============================================
# 1. 请求处理
# ============================================
"""
✅ 使用request对象获取请求数据
✅ 使用适当的参数获取方法
✅ 验证所有输入数据
✅ 处理文件上传时使用secure_filename
✅ 使用g对象存储请求级别数据
"""

# ============================================
# 2. 响应处理
# ============================================
"""
✅ API返回JSON格式响应
✅ 使用正确的HTTP状态码
✅ 设置适当的安全响应头
✅ 大文件使用流式响应
✅ 使用make_response构建复杂响应
"""

# ============================================
# 3. 错误处理
# ============================================
"""
✅ 注册全局错误处理器
✅ 使用自定义异常类
✅ 统一错误响应格式
✅ 记录错误日志
✅ 生产环境不暴露详细错误信息
"""

# ============================================
# 4. 上下文管理
# ============================================
"""
✅ 理解请求上下文和应用上下文
✅ 后台任务使用app_context
✅ 测试使用test_request_context
✅ 避免在全局作用域访问上下文代理
"""

# ============================================
# 5. 性能优化
# ============================================
"""
✅ 使用请求钩子进行预处理
✅ 使用缓存减少重复计算
✅ 异步处理耗时操作
✅ 合理使用中间件
✅ 监控请求处理时间
"""
相关推荐
zb200641203 小时前
Spring Boot spring-boot-maven-plugin 参数配置详解
spring boot·后端·maven
云霄IT3 小时前
安卓apk逆向之crc32检测打补丁包crc32_patcher.py
java·前端·python
小捏哩3 小时前
死锁检测组件的设计
linux·网络·数据结构·c++·后端
麦聪聊数据3 小时前
基于 SQL2API 架构快速发布 RESTful 接口
数据库·后端·sql·低代码·restful
极光代码工作室3 小时前
基于深度学习的中文文本情感分析系统
人工智能·python·深度学习·神经网络·nlp
龙侠九重天3 小时前
使用 OpenClaw 进行数据分析和可视化
大数据·人工智能·python·ai·信息可视化·数据分析·openclaw
Code blocks3 小时前
Firms-Java:NASA火灾卫星数据Java客户端开源
java·spring boot·后端·开源软件
敏编程3 小时前
一天一个Python库:soupsieve - CSS 选择器在 Beautiful Soup 中的力量
开发语言·css·python
树獭非懒3 小时前
Google A2UI:让 AI 智能体「开口说界面」
前端·人工智能·后端