首页/新闻资讯/正文详情

Starlette 响应体系全解:从 Response 基类到流式与文件响应的源码级实践

发布时间:2026/9/23 9:55:29 来源:云帆数科 栏目:资讯中心
Starlette 响应体系全解:从 Response 基类到流式与文件响应的源码级实践
Starlette 响应体系全解从 Response 基类到流式与文件响应的源码级实践【免费下载链接】starletteThe little ASGI framework that shines. 项目地址: https://gitcode.com/gh_mirrors/st/starletteStarlette 提供了一套完整的响应类家族统一通过向 ASGI 的send通道发送http.response.start与http.response.body消息来返回 HTTP 响应。本文以 docs/responses.md 为主线结合 starlette/responses.py 源码与 tests/test_responses.py 测试用例逐一剖析Response、HTMLResponse、PlainTextResponse、JSONResponse、RedirectResponse、StreamingResponse、FileResponse的设计与实战要点读完即可掌握如何构造带自动响应头的响应、如何操作 Cookie、如何定制 JSON 序列化、如何流式传输内容、如何利用文件响应的 Range 断点续传能力以及如何挂载后台任务。响应类总览一切皆可被调用的 ASGI 应用在 Starlette 中所有响应对象都实现了__call__(scope, receive, send)方法因此响应实例本身就是一个合法的 ASGI 应用。你可以在async def app(scope, receive, send)中直接await response(scope, receive, send)把它发送出去也可以把响应对象直接交给路由、TestClient 或任何 ASGI 服务器执行。Response基类位于 starlette/responses.py其发送流程在__call__中一目了然源码 L163-L170async def __call__(self, scope, receive, send): if scope[type] websocket: send self._wrap_websocket_denial_send(send) await send({type: http.response.start, status: self.status_code, headers: self.raw_headers}) await send({type: http.response.body, body: self.body}) if self.background is not None: await self.background()这段代码还透露了两个重要细节WebSocket 场景的拒绝响应当 scope 类型为websocket时响应消息会被_wrap_websocket_denial_send改写为websocket.http.response.start/websocket.http.response.body从而向客户端发送 HTTP 拒绝响应如 403/405这是 WebSocket 握手失败时的标准反馈机制后台任务响应体发送完毕后若构造时传入了background来自 starlette/background.py 的BackgroundTask会继续执行该任务——这是响应后清理/记账/通知等场景的官方挂载点。Response 基类签名与自动响应头Response的构造签名与文档一致Response(content, status_code200, headersNone, media_typeNone)content字符串或字节串源码render同时支持bytes | memoryviewL48-L53传入None时渲染为空字节串status_code整数 HTTP 状态码默认200headers字符串字典media_type媒体类型字符串例如text/html。此外源码中还有一个文档之外的隐藏参数background: BackgroundTask | None NoneL39用于挂载后台任务前文已述。Content-Length 与 Content-Type 的自动填充init_headersL55-L81负责生成响应头规则如下Content-Length 自动计算基于渲染后的self.body长度。但有三个例外不会填充Content-Length状态码小于 200如 1xx、状态码为204No Content、状态码为304Not ModifiedContent-Type 自动推导基于media_type若媒体类型以text/开头且尚未显式包含charset则自动追加; charsetutf-8Response.charset类属性默认utf-8去重保护如果你在headers中已显式提供content-length或content-typeStarlette 不会重复生成以你传入的值为准大小写规范化传入的 headers 键会被转成小写并以latin-1编码保存为raw_headersASGI 要求的list[tuple[bytes, bytes]]格式。以下是最小完整示例文档原样保留可直接作为独立 ASGI 应用运行from starlette.responses import Response async def app(scope, receive, send): assert scope[type] http response Response(Hello, world!, media_typetext/plain) await response(scope, receive, send)对应测试见 test_text_response 与 test_bytes_response字符串内容返回response.text字节内容如media_typeimage/png返回response.content。动态修改响应头响应对象还提供headers属性MutableHeaders发送前可随时增删改response Response(hello, world, media_typetext/plain, headers{x-header-1: 123}) response.headers[x-header-2] 789测试 test_response_headers 验证了这种先构造后修改的用法两个自定义头都会出现在最终响应中。Cookie 操作set_cookie 与 delete_cookieStarlette 在响应对象上提供了set_cookie方法L89-L132底层基于标准库http.cookies.SimpleCookie生成符合规范的Set-Cookie头并直接追加到raw_headers。完整签名如下Response.set_cookie(key, value, max_ageNone, expiresNone, path/, domainNone, secureFalse, httponlyFalse, samesitelax, partitionedFalse)参数类型说明keystrCookie 的键valuestrCookie 的值max_ageint可选Cookie 存活秒数负数或0会立即丢弃该 Cookieexpiresint / datetime可选整数表示距过期的秒数或直接传一个datetime对象源码中会通过format_datetime(expires, usegmtTrue)格式化为 GMT 字符串L107-L110pathstr可选Cookie 生效的路径子集默认/domainstr可选Cookie 有效的域名securebool可选仅在 SSL/HTTPS 请求下发送httponlybool可选禁止通过 JavaScript 的Document.cookie、XMLHttpRequest、RequestAPI 访问samesitestr可选取值lax、strict、none默认laxpartitionedbool可选跨站 Cookie 仅在最初设置的顶层上下文中可用CHIPS 规范仅 Python 3.14 支持否则抛出ValueError关于samesite源码 L120-L124 中有硬性断言非法值会在发送前直接AssertionError关于partitioned源码 L126-L129 在sys.version_info (3, 14)时抛出ValueError(Partitioned cookies are only supported in Python 3.14 and above.)测试 test_set_cookie_raises_for_invalid_python_version 也针对 3.14 环境做了跳过与校验。delete_cookie快速作废 Cookiedelete_cookie是对set_cookie的便捷封装L134-L152它将max_age0、expires0重新调用set_cookie从而让浏览器立即过期并删除该 CookieResponse.delete_cookie(key, path/, domainNone)签名中同样可以透传secure、httponly、samesite参数源码实现默认沿用lax。对应测试见 test_delete_cookie。HTMLResponse 与 PlainTextResponse最常用的两个快捷类这两个类都是Response的极简子类唯一的区别是预设了media_type类属性L173-L178HTMLResponsemedia_type text/html接收文本或字节返回 HTML 页面PlainTextResponsemedia_type text/plain返回纯文本。from starlette.responses import HTMLResponse async def app(scope, receive, send): assert scope[type] http response HTMLResponse(htmlbodyh1Hello, world!/h1/body/html) await response(scope, receive, send)from starlette.responses import PlainTextResponse async def app(scope, receive, send): assert scope[type] http response PlainTextResponse(Hello, world!) await response(scope, receive, send)由于media_type以text/开头基类的init_headers会自动补上charsetutf-8。这也解释了为什么PlainTextResponse常被框架内部用于返回错误文本——例如 starlette/routing.py 的 404 响应、L279 的 405 Method Not Allowed 响应以及 FileResponse 内部的 400/416 错误响应。JSONResponse默认序列化与自定义 renderJSONResponse接收任意可被 JSON 序列化的数据返回application/json编码的响应L181-L201。from starlette.responses import JSONResponse async def app(scope, receive, send): assert scope[type] http response JSONResponse({hello: world}) await response(scope, receive, send)其render方法揭示了默认序列化策略L194-L201def render(self, content: Any) - bytes: return json.dumps( content, ensure_asciiFalse, allow_nanFalse, indentNone, separators(,, :), ).encode(utf-8)要点ensure_asciiFalse非 ASCII 字符如中文不会被转义为\uXXXX直接以 UTF-8 输出体积更小、可读性更好allow_nanFalse序列化时遇到NaN/Infinity会直接抛出ValueError防止生成非法 JSONseparators(,, :)紧凑输出去掉多余空格测试 test_json_none_response 验证了JSONResponse(None)会正确返回字面量bnull即空响应也是合法 JSON。自定义 JSON 序列化子类化并覆写 render当默认的json.dumps不够用时——例如要序列化datetime、UUID等非标准对象或想换用更快的序列化库——最佳实践是子类化JSONResponse并覆写render方法。文档给出的 orjson 示例此处 orjson 为第三方包需自行安装from typing import Any import orjson from starlette.responses import JSONResponse class OrjsonResponse(JSONResponse): def render(self, content: Any) - bytes: return orjson.dumps(content)render的职责是把任意content变成bytes只要返回类型正确基类的Content-Length计算、响应头生成等逻辑都会照常工作。不过需要说明的是在绝大多数场景下应优先使用默认JSONResponse只有在微优化某个特定接口、或确实需要序列化非标准对象类型时才值得引入自定义 render。RedirectResponse默认 307 与 URL 编码RedirectResponse返回 HTTP 重定向默认状态码为307 Temporary RedirectL204-L213。307 与 302 的关键区别在于307 会保留原始请求的 HTTP 方法与请求体适合 POST 等非 GET 方法的重定向场景。from starlette.responses import PlainTextResponse, RedirectResponse async def app(scope, receive, send): assert scope[type] http if scope[path] ! /: response RedirectResponse(url/) else: response PlainTextResponse(Hello, world!) await response(scope, receive, send)实现要点构造时以空内容初始化contentb因此响应体的Content-Length为0测试 test_redirect_response_content_length_header 专门验证了这一点Location头由quote(str(url), safe:/%#?[]!$()*,;)生成L213保留 URL 中的合法保留字符。测试 test_quoting_redirect_response 验证了/I ♥ Starlette/会被正确编码为/I%20%E2%99%A5%20Starlette/url参数接受字符串或URL对象来自 starlette/datastructures.py。注意RedirectResponse没有独立的media_type参数若需要自定义媒体类型需通过headers传入。另外RedirectResponse在框架内部也有应用starlette/routing.py 会在末尾斜杠重定向redirect_slashesTrue时构造RedirectResponse(urlstr(redirect_url))把客户端导流到带斜杠的规范化路径。StreamingResponse流式传输异步或同步迭代器StreamingResponse接受异步生成器/异步迭代器或普通生成器/迭代器将响应体分块流式发送L222-L283适用于 SSE、大文件导出、长轮询、逐块生成的 HTML 等边生成边发送的场景。文档示例每个数字之间延迟 0.5 秒逐步推送from starlette.responses import StreamingResponse import asyncio async def slow_numbers(minimum, maximum): yield htmlbodyul for number in range(minimum, maximum 1): yield li%d/li % number await asyncio.sleep(0.5) yield /ul/body/html async def app(scope, receive, send): assert scope[type] http generator slow_numbers(1, 10) response StreamingResponse(generator, media_typetext/html) await response(scope, receive, send)同步迭代器与 file-like 对象源码构造逻辑L233-L236会判断传入内容是否为AsyncIterable是 → 直接作为body_iterator否同步迭代器→ 用iterate_in_threadpool包装来自 starlette/concurrency.py把同步迭代放到线程池中执行避免阻塞事件循环。因此file-like 对象例如open()返回的文件对象本质上是普通迭代器可以直接传给StreamingResponse进行流式读取。测试 test_sync_streaming_response 验证了同步生成器同样可用test_streaming_response_custom_iterator 与 test_streaming_response_custom_iterable 则验证了任意实现了__aiter__/__anext__的自定义异步迭代器/可迭代对象均可作为内容源。流式发送协议细节stream_response方法L248-L255是流式发送的核心先发送http.response.start逐块迭代非bytes的块会自动用charset默认 utf-8编码并以more_bodyTrue标记还有后续最后发送空的http.response.body且more_bodyFalse收尾。由于响应体不是一次性渲染的基类的自动Content-Length不会生效没有完整的 body 可计算长度流式响应默认使用 chunked 传输。客户端断连与 ASGI 版本适配源码针对 ASGI 协议版本做了分叉处理L265-L283当asgi.spec_version (2, 4)流式发送中若抛出OSError典型如对端已断开导致的写失败会转换为ClientDisconnect异常定义于 starlette/requests.py供上层精确捕获处理更早的协议版本通过create_collapsing_task_group来自 starlette/_utils.py同时监听http.disconnect消息并推进响应流一旦任一方完成即取消另一任务组。这保证了大响应流式推送时客户端中途断开不会让服务器悬挂。StreamingResponse同样支持background后台任务测试 test_streaming_response 验证了响应体1, 2, 3, 4, 5与后台任务6, 7, 8, 9各自独立完成。FileResponse异步文件流式响应与 HTTP Range 断点续传FileResponse异步流式传输文件其构造参数与其他响应类型不同L296-L329参数说明path要流式传输的文件路径headers自定义响应头字典media_type媒体类型字符串未指定时根据filename或path通过mimetypes.guess_type推断L314-L316推断失败时回退为application/octet-streamfilename若设置会出现在响应的Content-Disposition中实现下载文件名效果content_disposition_typeContent-Disposition的类型attachment默认触发下载或inline浏览器内联展示background后台任务源码扩展参数stat_result可选的预计算文件统计信息源码扩展参数传入后跳过运行时os.statfrom starlette.responses import FileResponse async def app(scope, receive, send): assert scope[type] http response FileResponse(statics/favicon.ico) await response(scope, receive, send)自动生成的响应头文件响应会自动包含四类头set_stat_headersL331-L339Content-Length来自stat_result.st_sizeLast-Modified来自文件 mtime格式化为 HTTP-dateETag由md5(str(st_mtime) - str(st_size))生成即修改时间 文件大小的哈希文件未变则 ETag 不变Accept-Ranges: bytes在__init__中通过setdefault添加L319声明支持字节范围请求。Content-Type则由传入的media_type或按文件名推断若filename非空还会生成Content-Disposition头L320-L326ASCII 安全时输出attachment; filenameexample.png含非 ASCII 字符如中文文件名你好.txt时输出 RFC 5987 风格的attachment; filename*utf-8...对应测试见 test_file_response 与 test_file_response_with_chinese_filename。运行时文件校验如果构造时未提供stat_result__call__会在线程池中执行os.stat并填充上述头L348-L359同时做两项校验文件不存在 →RuntimeError(fFile at path {self.path} does not exist.)路径不是普通文件如目录→RuntimeError(fFile at path {self.path} is not a file.)。测试 test_file_response_with_missing_file_raises_error 与 test_file_response_with_directory_raises_error 分别覆盖了这两种异常。HTTP Range 请求206 / 416 与多段响应FileResponse完整支持 HTTP 范围请求这是文档特别强调的能力请求带Range头且文件存在时返回206 Partial Content只传输请求的字节区间范围不合法如起始位置超出文件大小时返回416 Range Not Satisfiable并附带Content-Range: bytes */{file_size}头L372-L374同时支持If-Range条件判断L363-L365只有当If-Range值等于当前的Last-Modified或ETag时才处理 Range否则返回完整文件_should_use_rangeL456-L457。Range 处理逻辑__call__的 L361-L382分支如下无Range头或有If-Range但条件不满足→ 完整文件响应_handle_simpleRange头解析失败 → 400MalformedRangeHeader单个有效范围 →206单段响应_handle_single_range重写Content-Range: bytes {start}-{end-1}/{file_size}与新的Content-LengthL401-L418多个范围 →206且Content-Type变为multipart/byteranges; boundary...的多段响应_handle_multiple_rangesL420-L454boundary 由token_hex(13)生成每段携带独立的Content-Type与Content-Range。范围解析规则_parse_range_header/_parse_rangesL459-L528还有几个工程细节只支持bytes单位其他单位返回 400请求段数超过max_ranges 100时直接忽略 Range、返回完整文件支持-500末尾 500 字节这类无起始位置的后缀范围语法多个重叠范围会被合并避免重复传输空段如-与非数字段会被忽略。此外FileResponse对两类特殊请求做了优化HEAD 请求只发送响应头、不发送正文send_header_onlyL343 与 L389-L390测试 test_file_response_on_head_method 验证了响应头完整而 body 为空pathsend 扩展若 scope 的extensions中声明了http.response.pathsendASGI 服务器支持零拷贝发送则发送http.response.pathsend消息直接交付文件路径L344 与 L391-L392避免应用层逐块拷贝。文件读取以chunk_size 64 * 102464KB分块进行且全程通过anyio.open_file异步完成不阻塞事件循环。第三方响应EventSourceResponseServer-Sent Events除内置响应类外Starlette 生态中还有第三方实现的响应类典型代表是sse-starlette 提供的EventSourceResponse第三方包使用时需单独安装sse-starlette。它实现了 Server-Sent EventsSSE协议让服务器可以向客户端持续推送事件流——与 WebSocket 相比SSE 基于单向 HTTP 长连接无需额外协议握手、天然支持重连与事件 ID适合实时通知、日志推送、AI 流式对话等服务器单向推送场景。从实现角度可以理解为EventSourceResponse本质上是一个基于StreamingResponse之上的封装它负责把事件数据按 SSE 规范data:/event:/id:/retry:行格式编码成流式分块发送同时设置正确的Content-Type: text/event-stream与禁用缓冲的相关头。若你的应用已经用到了StreamingResponse的分块机制再理解 SSE 流式推送就会非常顺理成章。综合实践一个整合全部响应能力的路由示例把文档与源码中的要点组合起来一个同时展示 JSON、Cookie、重定向、流式与文件响应的应用可以这样组织from starlette.responses import ( FileResponse, JSONResponse, RedirectResponse, Response, StreamingResponse, ) async def app(scope, receive, send): assert scope[type] http path scope[path] if path /json: response JSONResponse({message: 你好Starlette}) # UTF-8 直出紧凑序列化 response.set_cookie(session, abc123, max_age3600, httponlyTrue, samesitelax) elif path /old: response RedirectResponse(url/json) # 默认 307 elif path /stream: async def gen(): for i in range(5): yield fchunk-{i}\n response StreamingResponse(gen(), media_typetext/plain) elif path /download: response FileResponse(statics/example.txt, filenameexample.txt) # 自动 ETag / Last-Modified / Range else: response Response(Not Found, status_code404, media_typetext/plain) await response(scope, receive, send)可以借助 tests/test_responses.py 中对应的测试test_redirect_response、test_set_cookie、test_file_response、test_streaming_response等来验证各响应类的实际输出头与状态码从而把文档描述落到可观测的代码行为上。总结Starlette 的响应体系围绕响应即 ASGI 应用这一核心设计展开Response基类负责Content-Length/Content-Type的自动填充与 Cookie 操作HTMLResponse/PlainTextResponse/JSONResponse提供最常用的快捷封装RedirectResponse以 307 完成安全重定向StreamingResponse打通异步与同步迭代器的分块传输而FileResponse则把静态文件服务、ETag 缓存校验、Range 断点续传、多段 multipart 响应与零拷贝发送集于一身。无论你在框架层面使用哪种响应都可以追溯到 starlette/responses.py 中的统一send协议实现并用 tests/test_responses.py 中的测试用例作为行为基准。【免费下载链接】starletteThe little ASGI framework that shines. 项目地址: https://gitcode.com/gh_mirrors/st/starlette创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Kepler.gl 数据容器升级指南:从 `allData` 二维数组迁移到 `DataContainerInterface`
Kepler.gl 数据容器升级指南:从 `allData` 二维数组迁移到 `DataContainerInterface`

数据可视化数据分析 【免费下载链接】kepler.gl Kepler.gl is a powerful open source geospatial analysis tool for large-scale data sets. 项目地址: https://gitcode.com/gh_mirrors/ke/kepler.gl 点击查看 免费下载 Kepler.gl 在数据层架构上完成了一次重要演… · 2026/9/23 9:55:29

高光谱数据预处理实战:基于Python的完整流程与参数详解
高光谱数据预处理实战:基于Python的完整流程与参数详解

简介:面向毕业设计、课程设计与高光谱研究场景,一套基于Python的高光谱数据预处理方法项目提供了标准正态变换、多元散射校正、Savitzky-Golay平滑滤波、滑动平均、一阶/二阶差分、小波变换、均值中心化、标准化、最大最小归一化、矢量归一化等经典算法&… · 2026/9/23 9:55:23

windows7和vista老项目入门到精通实战避坑指南
windows7和vista老项目入门到精通实战避坑指南

windows7和vista老项目入门到精通实战避坑指南 是不是也遇到过这种情况:手头有个老系统,基于windows7和vista开发,文档寥寥无几,看了一堆教程还是不会写项目,代码一跑就报错,配置改了三遍还是连不上数据库。这种“入门到精通… · 2026/9/23 9:55:23

5步搞定联想s720运维,最佳实践让项目落地不再难
5步搞定联想s720运维,最佳实践让项目落地不再难

5步搞定联想s720运维,最佳实践让项目落地不再难 看了一堆教程还是不会写项目?别急,这其实是90%初学者的通病。理论背得滚瓜烂熟,一到真实场景就卡壳。 真正的 最佳实践 ,不是让你背更多命令,而是建立一套可复用的运维思维。今天我们就以… · 2026/9/23 12:08:37

化纤面料全解析:从聚酯纤维到混纺,教你选对衣服不踩坑
化纤面料全解析:从聚酯纤维到混纺,教你选对衣服不踩坑

很多人在挑选衣服时,第一反应就是翻看吊牌上的成分表,看到“聚酯纤维”“锦纶”这些字眼,眉头就皱起来了。我做了十来年面料采购和开发,几乎每周都会被人问到“化纤面料到底是什么”“是不是就是塑料”“穿上是不是闷得慌”。今天… · 2026/9/23 12:08:37

2026 五大 AI 论文工具排行榜|应届生毕设横向测评
2026 五大 AI 论文工具排行榜|应届生毕设横向测评

2026 毕业季,AI 论文工具已经成为本科、硕士毕业生完成毕业论文的重要辅助。市面上各类工具定位差异明显,有的主打一站式全流程,有的只做文献精读,有的专注文本改写。为了方便应届生快速筛选,本次测评选取国内使用最多… · 2026/9/23 12:08:37

2026 AI 论文工具排行榜|应届生毕设实测榜单,一站式平台 okbiye 位列榜首
2026 AI 论文工具排行榜|应届生毕设实测榜单,一站式平台 okbiye 位列榜首

2026 毕业季,AI 论文辅助工具已经成为应届生撰写毕业论文的常用帮手。市面上各类工具数量众多,侧重点各不相同,有的主打文献检索,有的仅支持降重改写,还有的只提供英文润色功能。为方便大家快速筛选合适工具&#xff0… · 2026/9/23 12:08:37

汉英翻译器开发中3个致命坑:新手避坑指南
汉英翻译器开发中3个致命坑:新手避坑指南

汉英翻译器开发中3个致命坑:新手避坑指南 报错堆在控制台,StackTrace 长得像天书,点进去全是 IndexOutOfBoundsException 或者 NullPointerException… · 2026/9/23 12:08:30

BenchmarkDotNet 从源码构建完全指南:Visual Studio 与命令行双方案详解
BenchmarkDotNet 从源码构建完全指南:Visual Studio 与命令行双方案详解

BenchmarkDotNet 从源码构建完全指南:Visual Studio 与命令行双方案详解 【免费下载链接】BenchmarkDotNet Powerful .NET library for benchmarking 项目地址: https://gitcode.com/gh_mirrors/be/BenchmarkDotNet 导读 本文讲解如何从源码构建 BenchmarkD… · 2026/9/23 12:08:30

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码