FastAPI 返回额外 Status Codes直接使用 Response 实现 200/201 多状态响应【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi导读默认情况下 FastAPI 会把你path operation中返回的内容包装进JSONResponse并使用默认或显式声明的 HTTP 状态码本文介绍如何在主状态码之外返回额外状态码——直接返回带自定义status_code的Response并解析其与响应模型序列化、OpenAPI 文档生成之间的边界与取舍。默认响应行为FastAPI 为何总是返回 JSONResponse默认情况下FastAPI会使用JSONResponse返回响应把你从path operation中返回的内容放进该JSONResponse里。它使用的状态码要么是 HTTP 默认的200 OK要么是你在path operation装饰器如app.put(...)中显式设置的status_code。这一默认行为在源码中有清晰的印证在 fastapi/routing.py 中JSONResponse被定义为各类路由装饰器的默认response_class例如第 379、985、1185 行附近的Default(JSONResponse)并在实际生成响应时通过actual_response_class(content, **response_args)包装返回数据见 fastapi/routing.py。也就是说只要path operation的函数体返回的是一个普通 Python 对象dict、Pydantic 模型等FastAPI 会负责序列化并统一套上默认的状态码与application/json媒体类型。这让大多数接口只返回数据、不关心状态码的写法非常简单。额外状态码直接返回 Response 并自行设置 status_code如果你希望在同一条路径上除了主状态码之外还能返回额外的状态码做法是直接返回一个Response例如JSONResponse并在构造时显式设置额外的status_code。以更新upsert商品为例典型需求是当item_id对应的条目已存在时执行更新返回 HTTP200 OK当该条目此前不存在时把它当作新条目创建返回 HTTP201 Created。而path operation上声明的默认状态码只有一个无法覆盖创建这种场景于是需要借助直接返回JSONResponse的方式动态给出201。示例代码完整可运行版本见 docs_src/additional_status_codes/tutorial001_py310.py带Annotated风格的tutorial001_an_py310.py见 tutorial001_an_py310.pyfrom fastapi import Body, FastAPI, status from fastapi.responses import JSONResponse app FastAPI() items {foo: {name: Fighters, size: 6}, bar: {name: Tenders, size: 3}} app.put(/items/{item_id}) async def upsert_item( item_id: str, name: str | None Body(defaultNone), size: int | None Body(defaultNone), ): if item_id in items: item items[item_id] item[name] name item[size] size return item else: item {name: name, size: size} items[item_id] item return JSONResponse(status_codestatus.HTTP_201_CREATED, contentitem)代码要点导入from fastapi.responses import JSONResponse文档标注的高亮行 docs_src/additional_status_codes/tutorial001_an_py310.py 第 4 行命中已有条目直接return item走默认的200 OKJSONResponse序列化路径未命中条目return JSONResponse(status_codestatus.HTTP_201_CREATED, contentitem)高亮行 tutorial001_an_py310.py 第 25 行把想要的状态码201和已序列化好的内容一起直接交还客户端。使用status.HTTP_201_CREATED这类命名常量而非裸数字201能避免魔法数字并自带代码补全提示。status模块由 FastAPI 统一导出直接from fastapi import status即可。该写法在请求处理链中的位置之所以直接返回Response能生效是因为请求处理逻辑对返回值做了类型分支。在 fastapi/routing.py 中可以看到当端点函数执行完毕后若raw_response是Response实例含JSONResponseFastAPI 会原样返回它只做极少量的兜底处理如为空时补上后台任务raw_response.background只有当返回值不是Response时才会走默认的序列化、响应模型过滤与状态码包装流程。对应测试用例验证仓库中为该教程编写了参数化测试见 tests/test_tutorial/test_additional_status_codes/test_tutorial001.py覆盖两种行为def test_update(client: TestClient): response client.put(/items/foo, json{name: Wrestlers}) assert response.status_code 200, response.text assert response.json() {name: Wrestlers, size: None} def test_create(client: TestClient): response client.put(/items/red, json{name: Chillies}) assert response.status_code 201, response.text assert response.json() {name: Chillies, size: None}test_update命中已存在的foo验证返回200且字段被更新test_create对不存在的red发起创建验证返回201 Created内容为完整的新条目。这份测试同时印证了同一条路径可稳定返回两种状态码的事实。该测试通过参数化 fixture 同时运行了tutorial001_py310普通类型注解与tutorial001_an_py310Annotated风格两个版本说明两种写法等价。重要警告直接返回 Response 时不会做模型序列化当你在path operation中直接返回Response如上面的JSONResponse时该对象会被原样返回给客户端它不会再经过任何response_model、Pydantic 模型过滤或字段转换逻辑的二次序列化因此请确保content中已经包含了你想让客户端看到的所有数据如果你使用JSONResponse请确保content里的值本身是合法的 JSON如 dict、list、str、int、float、bool、None以及可被 JSON 编码的组合。换句话说直接返回Response意味着你主动接管了内容编排 序列化 状态码这一段职责FastAPI 不再替你兜底。若内容不是合法 JSONJSONResponse在序列化阶段会失败客户端得到的是错误响应而不是你期望的201。这一行为同样与源码逻辑一致在上文 fastapi/routing.py 的分支中命中isinstance(raw_response, Response)后直接赋值response raw_response完全绕过了serialize_response与响应模型过滤。技术细节fastapi.responses 与 starlette.responses 的关系你也可以写作from starlette.responses import JSONResponse。为什么两者皆可因为FastAPI只是把starlette.responses里的同名类作为fastapi.responses再导出re-export纯粹是为了方便开发者少记一个导入路径。在 fastapi/responses.py 中可以看到这类再导出语句from starlette.responses import FileResponse as FileResponse # noqa from starlette.responses import HTMLResponse as HTMLResponse # noqa from starlette.responses import JSONResponse as JSONResponse # noqa from starlette.responses import PlainTextResponse as PlainTextResponse # noqa from starlette.responses import RedirectResponse as RedirectResponse # noqa from starlette.responses import Response as Response # noqa from starlette.responses import StreamingResponse as StreamingResponse # noqa类似的还有status模块绝大多数可用的 Response 类和状态码常量都直接来自StarletteFastAPI 只是做了统一的便捷转发。因此在项目里混用fastapi.responses.JSONResponse与starlette.responses.JSONResponse是等价的选择一种风格保持一致即可。官方文档的示例统一使用fastapi.responses命名空间。注意虽然仓库代码中也保留了ORJSONResponse、UJSONResponse等历史类见 fastapi/responses.py但它们的 docstring 已明确标注 deprecated——现代 FastAPI 会在设置了返回类型/响应模型时由 Pydantic 直接序列化为 JSON 字节。日常开发建议直接使用标准JSONResponse无需引入第三方 JSON 库。OpenAPI 与 API 文档额外状态码默认不可见直接返回额外状态码和额外响应时这些内容不会进入 OpenAPI schema也就不会出现在交互式 API 文档里因为 FastAPI 无法在调用之前预知你会返回什么——请求处理时的动态分支属于运行时行为静态的 OpenAPI 生成阶段看不到它。因此使用该方式时请记住文档中的接口只会展示你在path operation中声明的主状态码本例如200动态返回的201 Created不会出现在/docs的响应示例与 Schema 中若希望这些额外的状态码以及对应的响应模型、媒体类型、描述被记录到 OpenAPI 与 API 文档中可以在代码层面使用Additional Responsesresponses参数进行声明详见同目录下的进阶文档 Additional Responses额外响应英文原版见 docs/en/docs/advanced/additional-responses.md。两条路线如何配合运行时返回直接return JSONResponse(status_codestatus.HTTP_201_CREATED, contentitem)——真正决定客户端收到什么文档声明在装饰器中用responses{201: {model: Item}}之类配置让 OpenAPI 记录该状态码的可能返回内容——决定文档里显示什么。二者并不冲突前者负责行为后者负责可发现性。若接口约定对外公开且需要稳定的 API 契约建议运行时直接返回 OpenAPI 显式声明一起使用。小结直接返回 Response 的适用边界适用场景同一条path operation需要根据业务分支返回不同状态码如本教程的200 OK更新与201 Created创建二选一或者你需要完全掌控响应内容与状态码例如返回202 Accepted、204 No Content、409 Conflict等语义化状态。注意事项直接返回的Response不再经过响应模型序列化与字段过滤必须自行保证内容完整且为合法 JSONOpenAPI 文档默认不会展示这些额外状态码如需入档请配合responses参数声明。可验证依据行为层面的证据来自 docs_src/additional_status_codes/tutorial001_py310.py 示例与 tests/test_tutorial/test_additional_status_codes/test_tutorial001.py 的双状态码测试底层实现证据来自 fastapi/routing.py 中返回值是否为Response的分支判断fastapi.responses与 Starlette 的关系证据来自 fastapi/responses.py 的再导出源码。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
