
FastAPI 自定义响应完全指南HTML、流式、文件、重定向与自定义 Response 类的实战与原理【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi本篇指南围绕 FastAPI 的自定义响应Custom Response机制展开核心材料取自 docs/ko/docs/advanced/custom-response.md英文源文档见 docs/en/docs/advanced/custom-response.md。读完本文你将掌握如何借助response_class声明非 JSON 的响应类型HTML、纯文本、重定向、流式、文件等如何直接返回Response对象及其与response_class的权衡如何通过继承Response编写带orjson等自定义逻辑的响应类以及如何为整个应用或APIRouter设置默认响应类。文中所有代码示例均来自仓库docs_src/custom_response/目录下的真实教程源码可直接复制运行。为什么需要response_class从 JSON 默认行为说起默认情况下FastAPI对每个路径操作path operation都返回 JSON 响应若在路径操作装饰器中声明了 响应模型Response ModelFastAPI 会使用 Pydantic 把返回数据序列化成 JSON若没有声明响应模型FastAPI 会使用 JSON 兼容编码器jsonable_encoder 把数据转换后再放进JSONResponse。在 直接返回响应 中我们看到你可以直接返回一个Response或其子类如JSONResponse来覆写默认行为。但这种方式有两个代价数据不再被自动转换——即便你声明了response_model也不会生效文档不再被自动生成——例如生成的 OpenAPI 中不会包含具体 media typeHTTP 头Content-Type等描述。因此路径操作装饰器提供了response_class参数让你显式声明希望使用的Response子类。你的路径操作函数返回的内容会被放进这个Response中送出。注意如果你使用了没有 media type 的响应类FastAPI 会认为该响应没有任何内容body因此不会在生成的 OpenAPI 文档中记录该响应的格式。JSON 响应的性能关键优先使用响应模型当路径操作装饰器中声明的response_class带有 JSON media typeapplication/json例如JSONResponse本身那么返回数据会先按你声明的 Pydanticresponse_model自动转换并过滤多余字段但 JSON 字节序列化这一步不是由 Pydantic 完成的数据先经jsonable_encoder转换再交给JSONResponse类由其调用 Python 标准库json序列化成字节。也就是说走response_classJSONResponse的路径比直接使用响应模型多了一层jsonable_encoder中间转换。追求最大性能的正确姿势是声明 响应模型且不要给装饰器传response_class。此时 FastAPI 直接以响应模型进行 JSON 序列化跳过了中间转换这也是官方的推荐写法from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class Item(BaseModel): name: str description: str | None None price: float tax: float | None None tags: list[str] [] app.post(/items/) async def create_item(item: Item) - Item: # 以返回类型标注作为响应模型 return item app.get(/items/) async def read_items() - list[Item]: return [ Item(namePortal Gun, price42.0), Item(namePlumbus, price32.0), ]完整示例见 docs_src/response_model/tutorial001_01_py310.py。HTML 响应HTMLResponse与response_class若要从FastAPI直接返回 HTML请使用HTMLResponse做法分两步导入HTMLResponse把它作为response_class传给路径操作装饰器。from fastapi import FastAPI from fastapi.responses import HTMLResponse app FastAPI() app.get(/items/, response_classHTMLResponse) async def read_items(): return html head titleSome HTML in here/title /head body h1Look ma! HTML!/h1 /body /html 完整示例见 docs_src/custom_response/tutorial002_py310.py。这里response_class还承担着定义响应 media type 的职责本例会把 HTTP 头Content-Type设为text/html该类型也会如实写进 OpenAPI 文档。直接在函数里返回Response正如 直接返回响应 中介绍的那样你也可以在路径操作函数内部直接构造并返回Response来覆写默认行为。上面的例子若改为手动返回HTMLResponse可以这样写from fastapi import FastAPI from fastapi.responses import HTMLResponse app FastAPI() app.get(/items/) async def read_items(): html_content html head titleSome HTML in here/title /head body h1Look ma! HTML!/h1 /body /html return HTMLResponse(contenthtml_content, status_code200)完整示例见 docs_src/custom_response/tutorial003_py310.py。警告由路径操作函数直接返回的Response不会被记录进 OpenAPI例如Content-Type不会被记录也不会出现在自动交互式文档中。当然实际返回给客户端的Content-Type头、状态码等都来自你返回的那个Response对象。既想在 OpenAPI 中记录、又想覆写响应如果你需要在函数内部覆写响应同时又希望 OpenAPI 记录该 media type可以把response_class参数和返回Response对象两者结合使用。此时response_class只用于 OpenAPI 文档记录而实际生效的是你返回的那个Response。下面把 HTML 的生成逻辑封装进一个辅助函数由其直接返回HTMLResponsefrom fastapi import FastAPI from fastapi.responses import HTMLResponse app FastAPI() def generate_html_response(): html_content html head titleSome HTML in here/title /head body h1Look ma! HTML!/h1 /body /html return HTMLResponse(contenthtml_content, status_code200) app.get(/items/, response_classHTMLResponse) async def read_items(): return generate_html_response()完整示例见 docs_src/custom_response/tutorial004_py310.py。在这个示例中generate_html_response()并不把 HTML 作为str返回而是直接生成并返回Response。由于generate_html_response()的结果会覆写 FastAPI 默认行为你其实已经手动接管了响应但正因为同时把HTMLResponse传给了response_classFastAPI 仍能正确地把该路径操作在 OpenAPI 与交互式文档中记录为text/html的 HTML 响应。效果如下可用的响应类型一览仓库文档列出以下常用响应类。需要记住的是你可以用Response返回任何内容甚至自己写子类。技术细节from starlette.responses import HTMLResponse同样可用。FastAPI只是出于方便把starlette.responses以fastapi.responses的形式重新导出见 fastapi/responses.py绝大多数可用响应类都直接来自 Starlette。基类ResponseResponse是基类其余响应类都继承自它。可以直接返回它它接受以下参数contentstr或bytesstatus_code表示 HTTP 状态码的intheaders字符串构成的dictmedia_type表示 media type 的str例如text/html。FastAPI准确说是其底层的 Starlette会自动附加Content-Length头同时根据media_type附加Content-Type头若是文本类型还会追加 charset。例如返回自定义 media type 的 XMLfrom fastapi import FastAPI, Response app FastAPI() app.get(/legacy/) def get_legacy_data(): data ?xml version1.0? shampoo Header Apply shampoo here. /Header Body Youll have to use soap here. /Body /shampoo return Response(contentdata, media_typeapplication/xml)完整示例见 docs_src/response_directly/tutorial002_py310.py。HTMLResponse接收一段文本或字节返回 HTML 响应行为如上面已演示的那样。PlainTextResponse接收一段文本或字节返回纯文本响应from fastapi import FastAPI from fastapi.responses import PlainTextResponse app FastAPI() app.get(/, response_classPlainTextResponse) async def main(): return Hello World完整示例见 docs_src/custom_response/tutorial005_py310.py。JSONResponse接收数据返回以application/json编码的响应即FastAPI的默认响应格式。技术细节若你声明了响应模型或返回类型标注则该模型会直接用于把数据序列化为 JSON并直接以正确的 JSON media type 返回而不会经过JSONResponse类。这也是获得最佳性能的理想方式。RedirectResponse返回 HTTP 重定向默认状态码为307临时重定向。它可以有两种用法直接返回RedirectResponsefrom fastapi import FastAPI from fastapi.responses import RedirectResponse app FastAPI() app.get(/typer) async def redirect_typer(): return RedirectResponse(https://typer.tiangolo.com)完整示例见 docs_src/custom_response/tutorial006_py310.py。把它用作response_class——此时路径操作函数只需直接返回 URL 字符串即可使用的状态码将是RedirectResponse的默认值307from fastapi import FastAPI from fastapi.responses import RedirectResponse app FastAPI() app.get(/fastapi, response_classRedirectResponse) async def redirect_fastapi(): return https://fastapi.tiangolo.com完整示例见 docs_src/custom_response/tutorial006b_py310.py。response_class与status_code组合使用装饰器的status_code参数会与response_class一起生效从而覆盖重定向的默认状态码例如下面的302from fastapi import FastAPI from fastapi.responses import RedirectResponse app FastAPI() app.get(/pydantic, response_classRedirectResponse, status_code302) async def redirect_pydantic(): return https://docs.pydantic.dev/完整示例见 docs_src/custom_response/tutorial006c_py310.py。StreamingResponse接收一个异步生成器或普通的生成器/迭代器带yield的函数并把响应体以流式方式发送给客户端import anyio from fastapi import FastAPI from fastapi.responses import StreamingResponse app FastAPI() async def fake_video_streamer(): for i in range(10): yield bsome fake video bytes await anyio.sleep(0) app.get(/) async def main(): return StreamingResponse(fake_video_streamer())完整示例见 docs_src/custom_response/tutorial007_py310.py。技术细节关于取消一个async任务只有在到达await处才能被取消。若生成器内部没有任何await它无法被正确取消甚至在取消请求发出后仍可能继续运行。上面这个小例子本身不需要任何await因此特意加入了await anyio.sleep(0)给事件循环一个处理取消的机会。这一点在大规模或无限流的场景下尤为重要。建议如果业务合适比起直接返回StreamingResponse更推荐参照 数据流式传输Stream Data 的风格编写那样更便捷并且取消逻辑在后台由框架替你处理。若要流式输出 JSON Lines可参考 JSON Lines 流式传输教程。FileResponse把文件以异步流式方式作为响应发送。它的构造参数与其它响应类型不同path要流式发送的文件路径headers以字典形式提供的自定义响应头media_type表示 media type 的字符串若未设置会根据文件名或路径推断filename若设置会包含在响应头的Content-Disposition中。文件响应会自动附带合适的Content-Length、Last-Modified和ETag头。直接返回FileResponsefrom fastapi import FastAPI from fastapi.responses import FileResponse some_file_path large-video-file.mp4 app FastAPI() app.get(/) async def main(): return FileResponse(some_file_path)完整示例见 docs_src/custom_response/tutorial009_py310.py。把它用作response_class——此时函数直接返回文件路径字符串即可from fastapi import FastAPI from fastapi.responses import FileResponse some_file_path large-video-file.mp4 app FastAPI() app.get(/, response_classFileResponse) async def main(): return some_file_path完整示例见 docs_src/custom_response/tutorial009b_py310.py。编写自定义响应类继承Response并实现render()你可以继承Response创建自定义响应类并使用它。典型场景是想让某些响应类以特定选项序列化数据。官方示例以第三方 JSON 库orjson为例希望返回带缩进、格式化的 JSON因此使用其orjson.OPT_INDENT_2选项。自定义响应类的关键点是实现Response.render(content)方法让它把内容转换为bytes返回from typing import Any import orjson from fastapi import FastAPI, Response app FastAPI() class CustomORJSONResponse(Response): media_type application/json def render(self, content: Any) - bytes: assert orjson is not None, orjson must be installed return orjson.dumps(content, optionorjson.OPT_INDENT_2) app.get(/, response_classCustomORJSONResponse) async def main(): return {message: Hello World}完整示例见 docs_src/custom_response/tutorial009c_py310.py。效果上原本会返回{message: Hello World}现在会返回带缩进的{ message: Hello World }从底层机制看类属性media_type application/json会被用来构造Content-Type头而 FastAPIStarlette在发送前会调用render(content)把内容序列化成字节——这就是自定义序列化逻辑的挂载点。比起格式化 JSON你当然能借此实现更有价值的自定义输出逻辑。自定义orjson响应与响应模型性能对比如果你的目标只是性能那么用 响应模型Response Model 通常会比自定义orjson响应更好。原因有二使用响应模型时FastAPI 直接用 Pydantic 把数据序列化为 JSON不经过jsonable_encoder这类中间转换步骤这些步骤在其它情况下会发生底层实现上Pydantic 在 JSON 序列化时使用的正是与orjson相同的 Rust 基础机制Pydantic v2 的pydantic-core所以仅用响应模型就已能拿到顶尖性能。全局默认响应类default_response_class在创建FastAPI实例或APIRouter时你可以通过default_response_class参数指定默认使用的响应类。下面的示例让整个应用在所有路径操作中默认使用HTMLResponse而不是 JSONfrom fastapi import FastAPI from fastapi.responses import HTMLResponse app FastAPI(default_response_classHTMLResponse) app.get(/items/) async def read_items(): return h1Items/h1pThis is a list of items./p完整示例见 docs_src/custom_response/tutorial010_py310.py。提示即使设置了默认响应类你仍可像之前那样在单个路径操作中用response_class覆盖它。这一参数同样适用于APIRouter(default_response_class...)便于按模块设定不同的默认响应风格。进一步在 OpenAPI 中补充更多响应文档除了response_class你还可以用 OpenAPI 的responses机制声明更丰富的 media type 与其它响应细节包括为单个路径声明多个不同的响应。详见仓库内的 OpenAPI 附加响应Additional Responses它对应的代码示例位于 docs_src/additional_responses 目录。小结response_class与直接返回 Response如何取舍方式数据自动转换OpenAPI 文档记录适用场景仅声明response_model不写response_class✅ 直接由 Pydantic 序列化性能最佳✅绝大多数 JSON API声明 JSON 类response_class如JSONResponse✅ 但多经过jsonable_encoder中间转换✅需要对 JSON 字节做额外定制时response_classHTMLResponse/PlainTextResponse/...✅ 按 media type 处理✅Content-Type会被记录HTML 页面、纯文本、文件下载、重定向等在函数内直接返回Response❌ 不会自动转换❌ 不会记录 media type需要完全手控响应对象如覆写头、状态码response_class 返回Response对象手动控制✅ 仍按response_class记录既要精确控制响应又要保留文档在FastAPI/APIRouter级别还可以通过default_response_class设置统一的默认响应。合理组合响应模型 response_class 直接返回Response三种手段就能让 FastAPI 项目在 JSON、HTML、流式、文件下载与自定义序列化等各类输出需求下既保证性能又保持 OpenAPI 文档的完整与准确。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考