bigywater.tech
Flask · 39

Response 对象构造过程

返回的响应对象是怎么构造出来的?状态码和状态行怎么同步?头是什么类型?头里放换行符会怎样?这篇逐项打印。

13
set_data("你好,世界") 之后的 Content-Length(UTF-8 字节数)
ValueError
往响应头里放换行符时的异常
不校验
Response 对状态码范围的检查(99、1000、abc 都能构造)

构造参数

实测 · 本机执行输出
  Response('hi')                                           → 200 OK | text/html; charset=utf-8 | b'hi' | {'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '2'}
  Response('hi', 201)                                      → 201 CREATED | text/html; charset=utf-8 | b'hi' | {'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '2'}
  Response('hi', '202 自定义')                                → 202 自定义 | text/html; charset=utf-8 | b'hi' | {'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '2'}
  Response('hi', mimetype='text/plain')                    → 200 OK | text/plain; charset=utf-8 | b'hi' | {'Content-Type': 'text/plain; charset=utf-8', 'Content-Length': '2'}
  Response('hi', content_type='text/csv')                  → 200 OK | text/csv | b'hi' | {'Content-Type': 'text/csv', 'Content-Length': '2'}
  Response('hi', content_type='text/plain; charset=latin-1') → 200 OK | text/plain; charset=latin-1 | b'hi' | {'Content-Type': 'text/plain; charset=latin-1', 'Content-Length': '2'}
  Response('hi', headers={'X-A': '1'})                     → 200 OK | text/html; charset=utf-8 | b'hi' | {'X-A': '1', 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '2'}
  Response(b'\x00\x01')                                    → 200 OK | text/html; charset=utf-8 | b'\x00\x01' | {'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '2'}
  Response(['a', 'b'])                                     → 200 OK | text/html; charset=utf-8 | b'ab' | {'Content-Type': 'text/html; charset=utf-8'}
  Response()                                               → 200 OK | text/html; charset=utf-8 | b'' | {'Content-Type': 'text/html; charset=utf-8'}

上一篇:Flask 与 Werkzeug 的关系(Response 来自 Werkzeug);下一篇:JSON Response 的序列化。视图返回值怎样变成 Response:make_response() 的响应转换过程。

status 与 status_code

实测 · 本机执行输出
  设 status_code=404 后 status = 404 NOT FOUND
  设 status="418 我是茶壶" 后 status_code = 418 | status = 418 我是茶壶
  Response(status=99) → 99 UNKNOWN / 99
  Response(status=1000) → 1000 UNKNOWN / 1000
  Response(status='999 x') → 999 x / 999
  Response(status='abc') → 0 abc / 0

headers

实测 · 本机执行输出
  类型: Headers
  结果: [('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '1'), ('x-one', '1b'), ('X-Multi', 'a'), ('X-Multi', 'b'), ('X-Ext', 'e')]
  getlist("X-Multi") = ['a', 'b'] | r.headers["x-one"] = 1b | "X-Ext" in headers: True
  删除 X-Ext 后: ['Content-Type', 'Content-Length', 'x-one', 'X-Multi', 'X-Multi']

头注入

实测 · 本机执行输出
  值里有 \n → ValueError: Header values must not contain newline characters.
  值里有 \r\n → ValueError: Header values must not contain newline characters.
  值里有 \x00 → 设置成功: 'a\x00b'

data 与 Content-Length

实测 · 本机执行输出
  初始 Content-Length = 5 | data = b'hello'
  set_data 之后 Content-Length = 13 (UTF-8 字节数) | 字符数 = 5
  迭代器形式的 response:Content-Length = None | is_sequence = True | is_streamed = False
  生成器形式:Content-Length = None | is_sequence = False | is_streamed = True

WSGI 输出

实测 · 本机执行输出
  GET: 200 OK [('X-A', '1'), ('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '11')] | 正文 [b'body \xe5\x86\x85\xe5\xae\xb9']
  HEAD: 200 OK | Content-Length = 11 | 正文 []

分步演示

Response 对象
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动
    • 没有验证:set_cookie 与 cookie 头(见第 46 篇)、response.freeze()、direct_passthrough 与 call_on_close 的行为、非 UTF-8 charset 的编码、HTTP/2 下状态行的呈现
    京ICP备2026067502号-1京公网安备11010502063487号