FastAPI 返回额外 Status Codes:直接使用 Response 实现 200/201 多状态响应
2026/9/8 21:21:20 网站建设 项目流程

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_codeResponse,并解析其与响应模型序列化、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.py):

from 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(default=None), size: int | None = Body(default=None), ): 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_code=status.HTTP_201_CREATED, content=item)

代码要点:

  1. 导入from fastapi.responses import JSONResponse(文档标注的高亮行 docs_src/additional_status_codes/tutorial001_an_py310.py 第 4 行);
  2. 命中已有条目:直接return item,走默认的200 OK+JSONResponse序列化路径;
  3. 未命中条目return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)(高亮行 tutorial001_an_py310.py 第 25 行),把想要的状态码201和已序列化好的内容一起直接交还客户端。

使用status.HTTP_201_CREATED这类命名常量而非裸数字201,能避免魔法数字并自带代码补全提示。status模块由 FastAPI 统一导出,直接from fastapi import status即可。

该写法在请求处理链中的位置

之所以"直接返回Response"能生效,是因为请求处理逻辑对返回值做了类型分支。在 fastapi/routing.py 中可以看到:当端点函数执行完毕后,若raw_responseResponse实例(含JSONResponse),FastAPI 会原样返回它,只做极少量的兜底处理(如为空时补上后台任务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 不再替你兜底。若内容不是合法 JSON,JSONResponse在序列化阶段会失败,客户端得到的是错误响应而不是你期望的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 类和状态码常量都直接来自Starlette,FastAPI 只是做了统一的便捷转发。因此在项目里混用fastapi.responses.JSONResponsestarlette.responses.JSONResponse是等价的,选择一种风格保持一致即可。官方文档的示例统一使用fastapi.responses命名空间。

注意:虽然仓库代码中也保留了ORJSONResponseUJSONResponse等历史类(见 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 Responses(responses参数)进行声明,详见同目录下的进阶文档 Additional Responses(额外响应)(英文原版见 docs/en/docs/advanced/additional-responses.md)。

两条路线如何配合

  • 运行时返回:直接return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)——真正决定客户端收到什么;
  • 文档声明:在装饰器中用responses={201: {"model": Item}}之类配置让 OpenAPI 记录该状态码的可能返回内容——决定文档里显示什么。

二者并不冲突:前者负责行为,后者负责可发现性。若接口约定对外公开且需要稳定的 API 契约,建议"运行时直接返回 + OpenAPI 显式声明"一起使用。

小结:直接返回 Response 的适用边界

  • 适用场景:同一条path operation需要根据业务分支返回不同状态码(如本教程的200 OK更新与201 Created创建二选一);或者你需要完全掌控响应内容与状态码,例如返回202 Accepted204 No Content409 Conflict等语义化状态。
  • 注意事项:直接返回的Response不再经过响应模型序列化与字段过滤,必须自行保证内容完整且为合法 JSON;OpenAPI 文档默认不会展示这些额外状态码,如需入档请配合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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询