:为 API 文档声明多状态码与多媒体类型)
FastAPI OpenAPI 附加响应Additional Responses为 API 文档声明多状态码与多媒体类型【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi本篇指南讲解 FastAPI 中responses参数的完整用法如何在 OpenAPI 模式与自动生成的 API 文档中声明附加的状态码、媒体类型、描述与示例涵盖基于 Pydantic 模型的响应、为同一响应追加多种媒体类型、response_model/status_code/responses信息合并以及用字典解包复用预定义响应。这是一个偏高级的主题若你刚开始接触 FastAPI 可能暂时用不到但在设计公开 API 时它能让客户端与代码生成工具获得远比只声明 200 成功响应更精确的契约。附加响应会进入 OpenAPI 模式但运行时仍需自己返回 ResponseFastAPI 允许你声明包含附加状态码additional status codes、媒体类型media types、描述descriptions等信息的附加响应。这些附加响应会被包含进 OpenAPI 模式schema中因此也会出现在自动生成的 API 文档/docs的 Swagger UI 与/redoc里。关键点在于声明只影响文档不影响运行时行为。对这些附加响应你必须自己在路径操作函数里返回一个带有对应状态码与内容的Response如JSONResponse。也就是说FastAPI 不会替你生成404 响应它只是把这一契约写进 OpenAPI而实际返回何种状态码、什么内容完全由你的业务代码决定。从源码看responses参数在路由创建时被规范化保存fastapi/routing.py 处route.responses responses or {}随后在生成 OpenAPI 时被逐条处理见下文源码视角小节。使用model声明附加响应responses{404: {model: Message}}你可以向路径操作装饰器如app.get、app.post传递responses参数。它接收一个dict键每个响应的状态码如200、404值包含该响应信息的一个dict。每个响应dict中可以有一个model键其值是一个 Pydantic 模型用法类似response_model。FastAPI 会取该模型生成它的 JSON Schema并放到 OpenAPI 中正确的位置。完整示例docs_src/additional_responses/tutorial001_py310.pyfrom fastapi import FastAPI from fastapi.responses import JSONResponse from pydantic import BaseModel class Item(BaseModel): id: str value: str class Message(BaseModel): message: str app FastAPI() app.get(/items/{item_id}, response_modelItem, responses{404: {model: Message}}) async def read_item(item_id: str): if item_id foo: return {id: foo, value: there goes my hero} return JSONResponse(status_code404, content{message: Item not found})要点状态码404的附加响应使用了 Pydantic 模型Message其 JSON Schema 会被写入 OpenAPI当item_id foo时正常返回Item数据否则必须直接返回JSONResponse携带status_code404和content{message: Item not found}。声明与运行时返回要保持一致文档才不会骗人。model键不是 OpenAPI 的一部分注意model键本身不属于 OpenAPI 规范。它只是 FastAPI 提供的便捷写法——FastAPI 从那里取出 Pydantic 模型生成 JSON Schema并放到正确的位置。这个正确的位置是content键内其值是另一个 JSON 对象/dict其中以媒体类型如application/json为键其值又是一个 JSON 对象其中有schema键其值正是从模型生成的 JSON Schema——这就是正确的位置。一个关键实现细节FastAPI 不会直接把 JSON Schema 内联进该位置而是在此处添加一个引用reference即$ref指向 OpenAPI 中其他位置components/schemas下的全局 JSON Schema。这样其他应用与客户端可以直接使用这些全局 JSON Schema还能支撑更好的代码生成工具等生态能力。生成的 OpenAPI 结构对于上述路径操作OpenAPI 中生成的responses如下$ref指向全局 schema{ responses: { 404: { description: Additional Response, content: { application/json: { schema: { $ref: #/components/schemas/Message } } } }, 200: { description: Successful Response, content: { application/json: { schema: { $ref: #/components/schemas/Item } } } }, 422: { description: Validation Error, content: { application/json: { schema: { $ref: #/components/schemas/HTTPValidationError } } } } } }可以看到同一个路径操作同时拥有404、200、422三组响应其中422是 FastAPI 在有请求参数/请求体时自动附加的Validation Error响应fastapi/openapi/utils.py。模型 Schema 则位于 OpenAPI 的另一处components/schemas中被引用{ components: { schemas: { Message: { title: Message, required: [message], type: object, properties: { message: { title: Message, type: string } } }, Item: { title: Item, required: [id, value], type: object, properties: { id: { title: Id, type: string }, value: { title: Value, type: string } } }, ValidationError: { title: ValidationError, required: [loc, msg, type], type: object, properties: { loc: { title: Location, type: array, items: {type: string} }, msg: { title: Message, type: string }, type: { title: Error Type, type: string } } }, HTTPValidationError: { title: HTTPValidationError, type: object, properties: { detail: { title: Detail, type: array, items: {$ref: #/components/schemas/ValidationError} } } } } } }源码视角responses参数的处理链路从仓库源码可以确认这条处理链路路由创建时保存并校验在 fastapi/routing.pyFastAPI 遍历route.responses.items()断言每个附加响应必须是dict若存在model键则断言该状态码允许携带响应体is_body_allowed_for_status_code并以modeserialization为模型创建响应字段response_field供后续序列化 schema 使用。生成 OpenAPI 时逐条处理在 fastapi/openapi/utils.pyFastAPI 深拷贝每个附加响应copy.deepcopy弹出非 OpenAPI 的model键process_response.pop(model, None)把状态码统一转为字符串str(additional_status_code).upper()特别地DEFAULT会被归一化为default然后若该状态码关联了模型字段则以route_response_media_type or application/json作为媒体类型将模型 schema 合并进content.media_type.schemadescription的取值优先级为附加响应中的description 已有响应中的descriptionhttp.client.responses中的标准状态文本 兜底Additional Response最后用deep_dict_update合并进 OpenAPI 操作对象并写回规范化后的description。也就是说无论你在responses里写多少附加信息model都会被剥离并以全局$ref形式接入这正是上一节看到$ref: #/components/schemas/Message的原因。为主要响应添加附加媒体类型content: {image/png: {}}借助同一个responses参数你还可以为同一个主要响应添加不同的媒体类型。例如声明你的路径操作既能返回 JSON 对象媒体类型application/json也能返回 PNG 图片媒体类型image/pngfrom fastapi import FastAPI from fastapi.responses import FileResponse from pydantic import BaseModel class Item(BaseModel): id: str value: str app FastAPI() app.get( /items/{item_id}, response_modelItem, responses{ 200: { content: {image/png: {}}, description: Return the JSON item or an image., } }, ) async def read_item(item_id: str, img: bool | None None): if img: return FileResponse(image.png, media_typeimage/png) else: return {id: foo, value: there goes my hero}完整代码见 docs_src/additional_responses/tutorial002_py310.py。两个关键注意点运行时返回方式返回图片时必须直接使用FileResponse(image.png, media_typeimage/png)对应image/png媒体类型返回 JSON 时则按常规返回 dict 或JSONResponse。文档声明只是契约实际返回哪一分支由img查询参数决定。媒体类型默认值规则除非在responses参数中显式指定了不同的媒体类型否则 FastAPI 默认假定该响应与主要响应类main response class使用相同的媒体类型默认application/json。但如果你指定了媒体类型为None的自定义响应类则对于任何关联了模型的附加响应FastAPI 会使用application/json。这一默认值逻辑同样体现在源码media_type route_response_media_type or application/json这一行fastapi/openapi/utils.py。组合多处信息response_modelstatus_coderesponses你可以把来自多个位置response_model、status_code、responses参数的响应信息组合起来。典型场景是用默认状态码200或按需自定义状态码配合response_model声明主要响应再用responses为同一个响应直接在 OpenAPI schema 中声明附加信息。FastAPI 会保留responses中的附加信息并将其与模型的 JSON Schema 合并。示例docs_src/additional_responses/tutorial003_py310.py404响应使用 Pydantic 模型并带自定义description200响应使用response_model但同时提供自定义examplefrom fastapi import FastAPI from fastapi.responses import JSONResponse from pydantic import BaseModel class Item(BaseModel): id: str value: str class Message(BaseModel): message: str app FastAPI() app.get( /items/{item_id}, response_modelItem, responses{ 404: {model: Message, description: The item was not found}, 200: { description: Item requested by ID, content: { application/json: { example: {id: bar, value: The bar tenders} } }, }, }, ) async def read_item(item_id: str): if item_id foo: return {id: foo, value: there goes my hero} else: return JSONResponse(status_code404, content{message: Item not found})在这里404同时拥有模型 schema$ref到Message与自定义描述The item was not found200在response_modelItem生成的 schema 之外又补充了description与example两者被合并进 OpenAPI所有内容会组合进你的 OpenAPI并显示在 API 文档中效果见下图的 Swagger UI 界面。从上图可以看到GET /items/{item_id}端点文档中同时列出了200Item requested by ID含 JSON 示例与404The item was not found两组响应信息客户端据此即可了解不同状态码的返回契约。复用预定义响应并与自定义响应合并**dict解包有时你希望某些预定义响应应用于很多路径操作同时每个路径操作又有各自需要的自定义响应。这时可以使用 Python 的字典解包unpacking技巧**dict_to_unpackold_dict { old key: old value, second old key: second old value, } new_dict {**old_dict, new key: new value}这里new_dict将包含old_dict的全部键值对再加上新的键值对{ old key: old value, second old key: second old value, new key: new value, }把这一技巧用到路径操作上docs_src/additional_responses/tutorial004_py310.pyfrom fastapi import FastAPI from fastapi.responses import FileResponse from pydantic import BaseModel class Item(BaseModel): id: str value: str responses { 404: {description: Item not found}, 302: {description: The item was moved}, 403: {description: Not enough privileges}, } app FastAPI() app.get( /items/{item_id}, response_modelItem, responses{**responses, 200: {content: {image/png: {}}}}, ) async def read_item(item_id: str, img: bool | None None): if img: return FileResponse(image.png, media_typeimage/png) else: return {id: foo, value: there goes my hero}这样404、302、403三个预定义响应被复用到该路径操作同时又追加了200的image/png媒体类型声明。当预定义集合与自定义项出现相同状态码时后出现的自定义项解包表达式右侧会覆盖左侧预定义中的同名键——这是dict解包的自然语义也可用于按路径操作覆盖默认响应描述。从框架能力看这一预定义 合并的模式还延伸到路由层APIRouter与include_router均支持responses参数且合并顺序为父路由的 responses 在前子级/当前传入的在后见 fastapi/routing.py 与 fastapi/routing.py因此你可以在路由级别定义共享的附加响应再在具体路径操作上做局部覆盖。仓库测试 tests/test_additional_responses_router.py 即覆盖了在路由与路径操作上声明responses并断言 OpenAPI 输出结构的场景。关于 OpenAPI 响应对象的更多信息若要了解响应中到底可以包含哪些内容请查阅 OpenAPI 规范本仓库文档对应 OpenAPI 3.1.0OpenAPI Responses Object包含Response ObjectOpenAPI Response Objectresponses参数中每个响应都可以直接包含该对象的任意字段包括description、headers、content在其中声明不同的媒体类型与 JSON Schema、links等。结合本文内容responses参数中的每个响应 dict 本质上就是 OpenAPIResponse Object的字段集合FastAPI 额外提供model这个便捷键用于从 Pydantic 模型自动生成 JSON Schema其余字段description、content、headers、links等都会被原样合并进 OpenAPI 输出fastapi/openapi/utils.py 中的deep_dict_update保证了这一点。小结与最佳实践声明不等于实现responses只影响 OpenAPI 与文档务必在路径操作里用JSONResponse、FileResponse等直接返回与声明一致的状态码和内容善用model键用 Pydantic 模型声明附加响应的 schemaFastAPI 会生成全局 JSON Schema 并通过$ref引用避免文档膨胀并利于代码生成工具注意媒体类型默认值未显式声明时附加响应沿用主要响应类的媒体类型默认application/json自定义响应类媒体类型为None时带模型的附加响应统一使用application/json组合与复用response_model、status_code、responses可以协同工作用**dict解包可在路由/路径操作之间复用预定义响应遵循 OpenAPI 规范description、content、headers、links等Response Object字段都可直接使用实现文档契约的精确表达。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考