Flask 请求生命周期

深入理解 Flask 完整请求生命周期(附源码级分析)

作者 :Flask 实战笔记

日期 :2026-06-09

标签Flask Python Web开发 后端 WSGI

阅读时长:约 18 分钟


前言

很多同学用 Flask 写了不少项目,但对于"一个 HTTP 请求从进入 Flask 到返回响应,中间到底经历了什么"这个问题,却说不清楚。

本文将从 WSGI 入口响应返回,按领域逐层拆解 Flask 的完整请求生命周期,并补充官方文档和大多数教程刻意略过的细节:

  • Application Context 和 Request Context 的区别与顺序
  • 多个 before_request / after_request 的处理顺序(含 Blueprint 级别)
  • after_requestteardown_request 的执行时机差异
  • Flask 信号系统(Signal)的使用场景
  • 源码级分析:Flask 如何驱动整条生命周期

目录


一、生命周期总览

一次 HTTP 请求经过以下层次,先看全局再逐层展开:

text 复制代码
客户端发起请求
      |
      v
+---------------------+
|    传输层            |  WSGI 服务器 + 中间件(Gunicorn / ProxyFix...)
+---------------------+
      |
      v
+---------------------+
|    上下文层          |  push AppContext  →  push RequestContext
+---------------------+
      |
      v
+---------------------+
|    路由层            |  Werkzeug URL Map 匹配  →  定位 View Function
+---------------------+
      |
      v
+---------------------+
|    钩子层 · 前置     |  before_request(可短路,详见第五节)
+---------------------+
      |
      v
+---------------------+
|    业务层            |  View Function  →  make_response()
+---------------------+
      |
      v
+---------------------+
|    钩子层 · 后置     |  after_request(逆序执行,详见第五节)
+---------------------+
      |
      v
响应写回客户端(session 在此时写入 Cookie)
      |
      v
+---------------------+
|    清理层            |  teardown_request  →  pop RequestContext
|                     |  teardown_appcontext  →  pop AppContext
+---------------------+

核心规律:Flask 生命周期是一个洋葱模型------上下文、钩子层层包裹,视图函数在最中心;请求进来时从外到内,清理时从内到外。


二、传输层:WSGI 入口与中间件

2.1 WSGI 本质

Flask 本质是一个 WSGI Application ,所有 HTTP 请求的真实入口是 wsgi_app。以下是 Flask 源码(flask/app.py)的核心逻辑:

python 复制代码
# flask/app.py  Flask.wsgi_app(略去类型注解)
def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)   # 构建请求上下文(尚未推入)
    error = None
    try:
        try:
            ctx.push()                            # 推入上下文(含 AppContext)
            response = self.full_dispatch_request()  # 路由 + 钩子 + 视图
        except Exception as e:
            error = e
            response = self.handle_exception(e)   # 触发 errorhandler
        except:
            error = sys.exc_info()[1]
            raise
        return response(environ, start_response)  # 写回 WSGI
    finally:
        ctx.pop(error)                            # 清理层:弹出上下文

full_dispatch_request 是整条生命周期的驱动器:

python 复制代码
# flask/app.py  Flask.full_dispatch_request
def full_dispatch_request(self):
    self._got_first_request = True
    try:
        request_started.send(self, _sync_wrapper=None)  # 触发信号
        rv = self.preprocess_request()   # 执行所有 before_request
        if rv is None:
            rv = self.dispatch_request() # 执行 View Function
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)     # make_response + after_request

environ 是标准 WSGI 字典,包含请求的全部原始信息:

键名 含义
REQUEST_METHOD GET / POST / PUT 等
PATH_INFO 请求路径 /user/1
QUERY_STRING 查询字符串 ?page=1
HTTP_* 所有 HTTP 请求头
wsgi.input 请求体(文件对象)

2.2 WSGI 中间件

中间件在 Flask 接管之前介入,对 environ 进行修改。生产环境中最常用的是 ProxyFix,用于修正反向代理后的真实 IP 和协议:

python 复制代码
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
# ProxyFix 修改 environ 后,才调用原始 Flask.wsgi_app
# 因此 request.remote_addr 已是真实客户端 IP,而非代理 IP

三、上下文层:双上下文机制

这是 Flask 设计的核心,也是最容易混淆的地方。大多数教程只提"请求上下文",实际上 Flask 有两个独立的上下文

3.1 两个上下文对比

属性 Application Context Request Context
代理对象 current_appg requestsession
生命周期 请求开始到结束(也可手动创建) 仅限单次请求
典型用途 数据库连接、配置访问 请求参数、Cookie
推入时机 先于 Request Context 后于 App Context
弹出时机 后于 Request Context 先于 App Context

3.2 推入与弹出顺序

ctx.push() 内部的逻辑(flask/ctx.py):

python 复制代码
# flask/ctx.py  RequestContext.push(核心逻辑)
def push(self) -> None:
    # 若当前没有 AppContext,自动推入一个
    app_ctx = _cv_app.get(None)
    if app_ctx is None or app_ctx.app is not self.app:
        app_ctx = self.app.app_context()
        app_ctx.push()                 # 先推 AppContext
        self._cv_app_tokens.append(...)

    self._cv_tokens.append(
        _cv_tokens.set(self)           # 再推 RequestContext
    )
    ...
text 复制代码
请求开始:push AppContext  →  push RequestContext
请求结束:pop  RequestContext  →  pop  AppContext

3.3 典型错误:在 AppContext 清理时访问 request

python 复制代码
@app.teardown_appcontext
def close_db(exception):
    db = g.pop('db', None)       # 正确:g 属于 AppContext,此时仍可用
    if db is not None:
        db.close()

    # 错误!此时 RequestContext 已弹出,request 不可访问
    # print(request.method)  →  RuntimeError: Working outside of request context

3.4 g 对象的生命周期

g 挂在 Application Context 上,但每次请求都是全新的 g,不能跨请求传递数据:

python 复制代码
@app.before_request
def load_user():
    # 将当前用户存入 g,仅在本次请求内有效
    g.current_user = get_user_from_token(request.headers.get('Authorization'))

@app.route('/profile')
def profile():
    return jsonify({'user': g.current_user.name})  # 同一请求内可用

四、路由层:URL 匹配

Flask 将路由委托给 Werkzeug 的 MapRule 系统。dispatch_request 内部逻辑:

python 复制代码
# flask/app.py  Flask.dispatch_request
def dispatch_request(self) -> ft.ResponseReturnValue:
    req = request_ctx.request
    app_exc = None
    try:
        rule = req.url_rule
        # 将路径参数转换为 Python 类型(如 <int:id> → int)
        return self.ensure_sync(
            self.view_functions[rule.endpoint]
        )(**req.view_args)
    except Exception as e:
        app_exc = e
        raise

路由匹配发生在 request_context() 构建时(ctx.push() 之前),通过 Werkzeug 的 Map.bind 完成:

python 复制代码
# URL 匹配等价逻辑
adapter = app.url_map.bind_to_environ(environ)
endpoint, view_args = adapter.match()   # 失败时直接抛出 HTTPException

4.1 路由失败的两种情况

失败原因 抛出异常 HTTP 状态码
路径不存在 NotFound 404
路径存在但 HTTP 方法不匹配 MethodNotAllowed 405
python 复制代码
@app.route('/user/<int:id>', methods=['GET'])
def get_user(id):
    ...

# GET  /user/1    →  200
# POST /user/1    →  405 MethodNotAllowed(不是 404!)
# GET  /user/abc  →  404 NotFound(int 转换失败)

4.2 路由转换器类型

python 复制代码
@app.route('/post/<int:id>')        # 整数
@app.route('/file/<path:filename>') # 含斜杠的路径
@app.route('/tag/<string:name>')    # 字符串(默认)
@app.route('/uid/<uuid:uid>')       # UUID

五、钩子层:多个 before / after 的执行顺序

这是整个生命周期中最重要、最容易踩坑的部分。

5.1 完整钩子管道流程图

下图展示了注册了多个钩子时的完整执行顺序,右侧标注了每个位置的典型应用:

text 复制代码
请求进入钩子管道
      |
      v
+------------------------------------------+  典型用途
| app.before_request #1(最先注册)         |  请求日志记录、生成 request_id
+------------------------------------------+
      |  若返回非 None → 短路 ──────────────────────────────────────┐
      v                                                             |
+------------------------------------------+                       |
| app.before_request #2(其次注册)         |  身份鉴权、解析 JWT   |
+------------------------------------------+                       |
      |  若返回非 None → 短路 ──────────────────────────────────────┤
      v                                                             |
+------------------------------------------+                       |
| bp.before_request(Blueprint 级,最后)   |  Blueprint 权限检查   |
+------------------------------------------+                       |
      |  若返回非 None → 短路 ──────────────────────────────────────┤
      v                                                             |
+==========================================+                       |
|         View Function                    |  your_code()          |
|         业务逻辑                          |                       |
+==========================================+                       |
      |                                                             |
      v                                                             |
+------------------------------------------+                       |
|          make_response()                 |  返回值转为 Response   |
+------------------------------------------+                       |
      |                                          ◄──────────────────┘
      v
+------------------------------------------+
| bp.after_request(Blueprint 级,最先执行)|  响应格式化、数据脱敏
+------------------------------------------+
      |
      v
+------------------------------------------+
| app.after_request #2(后注册先执行,逆序)|  安全响应头注入
+------------------------------------------+
      |
      v
+------------------------------------------+
| app.after_request #1(先注册后执行,逆序)|  CORS 头、响应压缩
+------------------------------------------+
      |
      v
      响应写回客户端(session 在此时写入 Cookie)
      |
      v
+------------------------------------------+
| teardown_request(无论异常必定执行)       |  释放 DB 连接、记录耗时
+------------------------------------------+

三条核心规律:

  1. before_request:按注册顺序 执行,app 级先于 Blueprint 级。任意一个返回非 None 则短路。
  2. after_request:按注册顺序的逆序执行,Blueprint 级先于 app 级。
  3. teardown_request:不属于请求/响应管道,无论是否发生异常、是否短路,必定执行。

5.2 源码验证:before_request 的执行逻辑

python 复制代码
# flask/app.py  Flask.preprocess_request
def preprocess_request(self):
    # names 顺序:先 None(app 级),再当前 Blueprint 名称
    names = (None, *reversed(request.blueprints))

    for name in names:
        if name in self.before_request_funcs:
            for func in self.before_request_funcs[name]:
                rv = self.ensure_sync(func)()
                if rv is not None:
                    return rv   # 短路:直接返回,后续钩子和 View 都不执行
    return None

5.3 源码验证:after_request 的执行逻辑

python 复制代码
# flask/app.py  Flask.process_response
def process_response(self, response):
    # names 顺序:先 Blueprint 名称,再 None(app 级)------与 before 相反
    names = (*request.blueprints, None)

    for name in names:
        if name in self.after_request_funcs:
            # reversed:同一作用域内,后注册的先执行
            for func in reversed(self.after_request_funcs[name]):
                response = self.ensure_sync(func)(response)

    session_interface.save_session(self, session, response)  # session 写入 Cookie
    return response

5.4 多个 before_request 注册示例

python 复制代码
api_bp = Blueprint('api', __name__, url_prefix='/api')

# --- before_request:注册顺序 = 执行顺序 ---

@app.before_request
def log_request():                          # 第 1 个执行
    g.start_time = time.time()
    app.logger.info(f"→ {request.method} {request.path}")

@app.before_request
def authenticate():                         # 第 2 个执行
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Unauthorized'}), 401  # 短路,后续全跳过
    g.current_user = verify_token(token)

@api_bp.before_request
def check_permission():                     # 第 3 个执行(Blueprint 级,最后)
    if not g.current_user.has_permission('api'):
        return jsonify({'error': 'Forbidden'}), 403

# --- after_request:注册顺序的逆序执行 ---

@app.after_request
def add_cors(response):                     # 注册第 1 个,执行第 3 个(最后)
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

@app.after_request
def add_security_headers(response):         # 注册第 2 个,执行第 2 个
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

@api_bp.after_request
def format_response(response):              # Blueprint 级,执行第 1 个(最先)
    response.headers['Content-Type'] = 'application/json; charset=utf-8'
    return response

# --- teardown_request:无论发生什么,必定执行 ---

@app.teardown_request
def log_and_cleanup(exception):
    duration = time.time() - g.get('start_time', time.time())
    app.logger.info(f"← {response.status_code} ({duration:.3f}s)")
    db.session.remove()                     # 释放数据库连接

5.5 短路时的执行情况

text 复制代码
正常请求:
  before #1 → before #2 → bp.before → your_code() → bp.after → after #2 → after #1

before #2 触发短路(如鉴权失败):
  before #1 → before #2 [返回 401] → bp.after → after #2 → after #1
  (View Function 和 bp.before 被跳过;after_request 依然执行)

异常被 errorhandler 捕获:
  before #1 → before #2 → bp.before → your_code() [抛异常]
           → errorhandler 处理
           → after_request 不执行
           → teardown_request 执行

5.6 errorhandler 触发时各钩子的执行情况

钩子 正常请求 before 短路 errorhandler 触发
before_request 执行 部分执行 执行
your_code() 执行 跳过 跳过
after_request 执行 执行 不执行
teardown_request 执行 执行 执行

六、业务层:视图函数与响应生成

6.1 make_response 自动转换

View Function 的返回值不必是 Response 对象,finalize_request 通过 make_response() 自动转换:

python 复制代码
# flask/app.py  Flask.finalize_request
def finalize_request(self, rv, from_error_handler=False):
    response = self.make_response(rv)   # 统一转换为 Response 对象
    try:
        response = self.process_response(response)  # 执行 after_request
        request_finished.send(self, response=response, _sync_wrapper=None)
    except Exception:
        if not from_error_handler:
            raise
        ...
    return response

常见返回值的转换规则:

python 复制代码
return "Hello World"                        # Response("Hello World", 200, text/html)
return "Created", 201                       # Response("Created", 201)
return {"key": "value"}                     # jsonify 转换(Flask 2.2+)
return jsonify({"key": "value"}), 200       # 显式 JSON 响应
return redirect(url_for('index'))           # 302 重定向
return render_template('index.html', **ctx) # 渲染后的 HTML
return make_response("body", 200, {"X-Custom": "val"})  # 完全自定义

6.2 在 after_request 中修改响应

python 复制代码
@app.after_request
def add_security_headers(response):
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response  # 必须 return,否则响应变为 None,触发 500

6.3 errorhandler 注册

python 复制代码
@app.errorhandler(404)
def not_found(e):
    return render_template('errors/404.html'), 404

@app.errorhandler(Exception)
def handle_exception(e):
    if isinstance(e, HTTPException):
        return e
    app.logger.exception(e)
    return jsonify({'error': 'Internal Server Error'}), 500

七、清理层:资源释放与上下文弹出

7.1 teardown_request

teardown_request 无论请求是否发生异常都会执行,是释放请求级资源的正确位置。源码中由 ctx.pop() 触发:

python 复制代码
# flask/ctx.py  RequestContext.pop(核心逻辑)
def pop(self, exc=_sentinel):
    ...
    rv = _cv_tokens.pop()
    app.do_teardown_request(exc)           # 触发所有 teardown_request
    request_tearing_down.send(app, exc=exc)
    ...
    if app_ctx is not None:
        app_ctx.pop(exc)                   # 触发 teardown_appcontext
python 复制代码
@app.teardown_request
def shutdown_db_session(exception):
    if exception:
        db.session.rollback()
        app.logger.error(f"Request failed: {exception}")
    db.session.remove()  # 无论如何都释放连接

7.2 teardown_appcontext

teardown_request 之后、Application Context 弹出之前执行:

python 复制代码
@app.teardown_appcontext
def close_connection(exception):
    conn = g.pop('conn', None)
    if conn is not None:
        conn.close()

7.3 Session 写入时机

session 的序列化和 Cookie 写入由 process_responseafter_request 阶段末尾)完成,在 teardown_request 之前,因此在 teardown 中修改 session 无效:

python 复制代码
@app.teardown_request
def bad_session_write(exception):
    session['key'] = 'value'  # 无效:session 已在 after_request 阶段写入 Cookie

八、扩展:信号系统 Signal

Flask 内置了基于 Blinker 的信号系统,允许在生命周期各节点插入观察者,但不影响请求流程

使用前需确保安装了 Blinker:

bash 复制代码
pip install blinker

8.1 完整信号列表

Flask 提供以下内置信号,按生命周期顺序排列:

信号名 所在模块 触发时机 回调参数
appcontext_pushed flask.appcontext_pushed AppContext 推入后 sender=app
request_started flask.request_started RequestContext 推入后,before_request 前 sender=app
before_render_template flask.before_render_template 模板渲染前(render_template 调用时) sender=app, template=template, context=context
template_rendered flask.template_rendered 模板渲染完成后 sender=app, template=template, context=context
got_request_exception flask.got_request_exception 异常被 handle_exception 处理前 sender=app, exception=e
request_finished flask.request_finished 响应已生成,after_request 执行后 sender=app, response=response
request_tearing_down flask.request_tearing_down teardown_request 执行时 sender=app, exc=exception
appcontext_tearing_down flask.appcontext_tearing_down teardown_appcontext 执行时 sender=app, exc=exception
appcontext_popped flask.appcontext_popped AppContext 弹出后 sender=app
message_flashed flask.message_flashed flash() 被调用时 sender=app, message=message, category=category

8.2 信号与生命周期的对应位置

text 复制代码
push AppContext
      |
      v
  [appcontext_pushed]
      |
  push RequestContext
      |
      v
  [request_started]            ← before_request 前
      |
  before_request → View Function → after_request
      |                |
      |          (若调用 render_template)
      |                |
      |        [before_render_template]
      |                |
      |        [template_rendered]
      |
      v
  [request_finished]           ← after_request 后,响应写回前
      |
  响应写回客户端
      |
      v
  [request_tearing_down]       ← teardown_request 时
      |
  pop RequestContext
      |
      v
  [appcontext_tearing_down]    ← teardown_appcontext 时
      |
  [appcontext_popped]
      |
  pop AppContext

  (异常路径)
  View Function 抛出异常
      |
      v
  [got_request_exception]      ← errorhandler 处理前
      |
  errorhandler 处理 → finalize_request

8.3 案例一:请求耗时监控(APM)

python 复制代码
import time
from flask import request_started, request_finished, g, request

@request_started.connect_via(app)
def on_request_started(sender, **kwargs):
    g.request_start_time = time.time()

@request_finished.connect_via(app)
def on_request_finished(sender, response, **kwargs):
    duration = time.time() - g.request_start_time
    # 记录慢请求
    if duration > 1.0:
        app.logger.warning(
            f"Slow request: {request.method} {request.path} "
            f"took {duration:.3f}s, status={response.status_code}"
        )
    # 写入 Prometheus / StatsD
    metrics.histogram('http_request_duration_seconds', duration, tags={
        'method': request.method,
        'endpoint': request.endpoint,
        'status': response.status_code
    })

8.4 案例二:异常上报(Sentry 集成)

python 复制代码
from flask import got_request_exception
import sentry_sdk

@got_request_exception.connect_via(app)
def capture_exception(sender, exception, **kwargs):
    # 在 errorhandler 处理之前捕获,保留原始调用栈
    sentry_sdk.capture_exception(exception)

8.5 案例三:模板渲染审计

python 复制代码
from flask import template_rendered

@template_rendered.connect_via(app)
def on_template_rendered(sender, template, context, **kwargs):
    # 开发环境下打印模板渲染信息,排查渲染问题
    app.logger.debug(
        f"Template rendered: {template.name}, "
        f"context keys: {list(context.keys())}"
    )

8.6 案例四:Flash 消息审计

python 复制代码
from flask import message_flashed

@message_flashed.connect_via(app)
def on_flash(sender, message, category, **kwargs):
    # 记录所有 flash 消息,方便排查用户反馈
    app.logger.info(f"Flash [{category}]: {message}")

8.7 案例五:请求生命周期调试(开发环境)

python 复制代码
from flask import (
    request_started, request_finished,
    request_tearing_down, appcontext_tearing_down, request
)

if app.debug:
    @request_started.connect_via(app)
    def debug_request_started(sender, **kwargs):
        print(f"[Signal] request_started: {request.method} {request.path}")

    @request_finished.connect_via(app)
    def debug_request_finished(sender, response, **kwargs):
        print(f"[Signal] request_finished: status={response.status_code}")

    @request_tearing_down.connect_via(app)
    def debug_tearing_down(sender, exc, **kwargs):
        print(f"[Signal] request_tearing_down: exception={exc}")

    @appcontext_tearing_down.connect_via(app)
    def debug_appcontext_tearing_down(sender, exc, **kwargs):
        print(f"[Signal] appcontext_tearing_down: exception={exc}")

8.8 案例六:自定义信号

除了内置信号,你也可以创建自定义信号用于应用内部解耦:

python 复制代码
from blinker import Namespace

# 定义自定义信号
app_signals = Namespace()
order_created = app_signals.signal('order-created')
user_registered = app_signals.signal('user-registered')

# 发送信号(在业务逻辑中)
@app.route('/orders', methods=['POST'])
def create_order():
    order = Order.create(request.json)
    db.session.commit()
    # 触发信号,解耦后续逻辑
    order_created.send(app, order=order, user=g.current_user)
    return jsonify(order.to_dict()), 201

# 订阅信号(独立模块)
@order_created.connect_via(app)
def send_order_email(sender, order, user, **kwargs):
    """发送订单确认邮件"""
    mail.send(to=user.email, subject=f"订单 #{order.id} 确认", ...)

@order_created.connect_via(app)
def notify_warehouse(sender, order, **kwargs):
    """通知仓库备货"""
    warehouse_api.notify(order_id=order.id, items=order.items)

@order_created.connect_via(app)
def update_analytics(sender, order, **kwargs):
    """更新统计数据"""
    analytics.track('order_created', amount=order.total)

8.9 信号 vs 钩子:核心区别

维度 钩子(Hook) 信号(Signal)
能否干预流程 可以(before_request 可短路) 只读观察,不能干预
能否修改响应 可以(after_request) 不能
注册方式 @app.before_request 装饰器 signal.connect_via(app)
多个订阅者 有严格顺序 无保证顺序,彼此独立
异常处理 异常直接中断流程 单个订阅者异常不影响其他订阅者
适用场景 鉴权、预处理、响应头注入 监控、日志、APM、业务解耦
跨应用 与 Flask 实例绑定 可以跨模块、跨扩展订阅

九、生产中最常踩的坑

坑 1:资源释放放错钩子

python 复制代码
# 错误:after_request 在异常时不执行,数据库连接可能泄漏
@app.after_request
def release_db(response):
    db.session.remove()
    return response

# 正确:teardown_request 无论是否异常都执行
@app.teardown_request
def release_db(exception):
    db.session.remove()

坑 2:误以为 g 是全局共享对象

python 复制代码
# 错误:每次请求都是新的 g,跨请求数据会丢失
@app.before_request
def setup():
    g.cache = {}

# 如需跨请求共享数据,使用 Redis / 数据库 / Flask-Caching

坑 3:Blueprint 钩子影响范围误解

python 复制代码
# Blueprint 的 after_request 只对该 Blueprint 的路由生效
@auth_bp.after_request
def add_header(response):
    response.headers['X-Auth'] = 'true'
    return response
# 其他 Blueprint 的响应不会带这个 header

# 如需全局生效,注册在 app 上
@app.after_request
def add_header(response):
    response.headers['X-Auth'] = 'true'
    return response

坑 4:before_request 短路后 after_request 是否还执行

python 复制代码
# 短路时:after_request 依然执行(因为 finalize_request 仍被调用)
# 但是:errorhandler 触发时,after_request 不执行

@app.before_request
def auth():
    return jsonify({'error': 'Unauthorized'}), 401
    # after_request 会执行(短路返回的也是正常响应流程)

# 对比:
@app.route('/crash')
def crash():
    raise ValueError("unexpected error")
    # errorhandler 处理后,after_request 不执行
    # 请求日志等关键逻辑应放在 teardown_request

坑 5:after_request 忘记 return response

python 复制代码
@app.after_request
def process(response):
    response.headers['X-Custom'] = 'value'
    # 忘记 return → 响应变为 None → 500 Internal Server Error
    return response  # 必须 return

十、总结

层次 核心组件 关键行为
传输层 WSGI 服务器、中间件 中间件在 Flask 之前修改 environ
上下文层 AppContext、RequestContext App 先推后弹,Request 后推先弹
路由层 Werkzeug URL Map 路径不存在→404,方法不匹配→405
钩子层 before / after / teardown before 顺序执行可短路;after 逆序执行,异常时不执行;teardown 必执行
业务层 View Function、make_response 返回值自动转换;session 在 after_request 末尾写入 Cookie
清理层 teardown_request/appcontext 资源释放的正确位置;内到外的弹出顺序

参考资料


如有错误,欢迎评论区指出。

相关推荐
英豪1632 小时前
@Target + @Retention + isAnnotationPresent + getAnnotation
后端
黄同学real2 小时前
HJL WebAPI 项目日志入库实战:从建表到自动清理
后端
孟陬2 小时前
国外技术周刊 #140:在 Jeff Bezos 的私密 Campfire 峰会上,我学到了关于亿万富翁的事
前端·后端
站大爷IP2 小时前
那天,我的Python函数死活改不了全局变量
python
右耳朵猫AI2 小时前
Python周刊2026W22 | Django 6.1 Alpha 1发布、Nuitka 4.1发布、PEP 831终稿、PEP 808已接受
开发语言·python·django
小闹5492 小时前
CLAUDE CODE生成可视化数据库工具
后端
Wonderful U2 小时前
Python+Django实战|美食菜谱分享与食材采购一体化系统:食谱发布收藏、图文教程、食材商城、购物车、订单管理、美食点评、智能食谱推荐
python·django·美食
秦jh_2 小时前
【LangChain核心组件】少样本提示(示例选择器)
人工智能·python·langchain