bigywater.tech
Flask · 4

Flask Request Context

请求进来之后,request 为什么能在任何地方被直接读到?请求上下文什么时候建立、里面有什么、能不能带进线程?这篇用 test_request_context 把它拆开看。

2
承载上下文的 ContextVar 个数(_cv_app 与 _cv_request)
404
push 时只记录、分发时才抛出的路由异常
0
裸线程里能读到的请求上下文个数

没有请求上下文

实测 · 本机执行输出
  request.path   → RuntimeError: Working outside of request context. This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.
  session        → RuntimeError: Working outside of request context. This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.

上一篇:Flask Application Context;下一篇:current_app、request、g 的实现。

test_request_context

实测 · 本机执行输出
  源码:
      1 | with app.test_request_context('/a/b?x=1&y=2', method='POST', data={'k': 'v'}, headers={'X-T': '1'}):
      2 |     print(request.method, request.path, dict(request.args), dict(request.form), request.headers['X-T'])
      3 |     print([k for k in request.environ if k.isupper()][:8], len(request.environ))
  POST /a/b {'x': '1', 'y': '2'} {'k': 'v'} 1
  environ 里的大写键: ['REQUEST_METHOD', 'SCRIPT_NAME', 'PATH_INFO', 'QUERY_STRING', 'REQUEST_URI', 'RAW_URI', 'SERVER_NAME', 'SERVER_PORT', 'HTTP_HOST', 'SERVER_PROTOCOL', 'CONTENT_TYPE', 'CONTENT_LENGTH', 'HTTP_X_T']
  environ 里 wsgi.* 键: ['wsgi.version', 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors', 'wsgi.multithread', 'wsgi.multiprocess', 'wsgi.run_once']

push 做了什么

实测 · 本机执行输出
  push 之前 has_app_context / has_request_context: False False | ctx.url_adapter 类型: MapAdapter
  push 之前读 ctx.session → AssertionError: The session has not yet been opened.
  push 之后: True True | request.url_rule = /user/<int:uid> | view_args = {'uid': 42} | session 类型 = SecureCookieSession
  pop 之后: False False

路由异常

实测 · 本机执行输出
  push 成功;request.url_rule = None | routing_exception = <NotFound '404: Not Found'>
  dispatch_request() 才抛出: NotFound - 404 Not Found: The requested URL was not found on the server. If you entered the

session 的打开

实测 · 本机执行输出
  session 类型: NullSession | session.get("a") = None
  写入 → The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.
  配了 SECRET_KEY 后: SecureCookieSession {'a': 1}

线程里的请求上下文

实测 · 本机执行输出
  裸线程: RuntimeError: Working outside of request context.

This typically means th
  copy_current_request_context: / (线程里读到)

测试里保留上下文

实测 · 本机执行输出
  响应返回之后仍可访问:request.path = /set | request.args = {'x': '1'} | session = {'u': 'ann'}
  with 块之后 has_request_context = False

两个 ContextVar

实测 · 本机执行输出
  _cv_app = <ContextVar name='flask.app_ctx' at 0x109d85260> | _cv_request = <ContextVar name='flask.request_ctx' at 0x10a866c50>
  请求之外 _cv_request.get(None) = None
  请求内 _cv_request.get() 的类型: flask.ctx.RequestContext | ctx.app.name = x | ctx.request 类型: Request

分步演示

请求上下文
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:真实 HTTP 请求进入时 wsgi_app 里构造 environ 与上下文的完整过程(下一篇与生命周期篇继续看)、请求上下文在异步视图里的传递、preserve_context 在 Flask 其它版本里的名称与行为差异
    京ICP备2026067502号-1京公网安备11010502063487号