直接返回响应¶
当你创建一个 FastAPI 路径操作 时,你可以正常返回以下任意一种数据:dict,list,Pydantic 模型,数据库模型等等。
如果你声明了 响应模型,FastAPI 会使用它通过 Pydantic 将数据序列化为 JSON。
如果你没有声明响应模型,FastAPI 会使用在 JSON 兼容编码器 中阐述的 jsonable_encoder。
然后,FastAPI 会在后台将这些兼容 JSON 的数据(比如字典)放到一个 JSONResponse 中,该 JSONResponse 会用来发送响应给客户端。
但是你可以在你的 路径操作 中直接返回一个 JSONResponse。
提示
通常使用 响应模型 会比直接返回 JSONResponse 拥有更好的性能,因为它会在 Rust 中使用 Pydantic 序列化数据。
返回 Response¶
事实上,你可以返回任意 Response 或者任意 Response 的子类。
信息
JSONResponse 本身是一个 Response 的子类。
当你返回一个 Response 时,FastAPI 会直接传递它。
FastAPI 不会用 Pydantic 模型做任何数据转换,不会将响应内容转换成任何类型,等等。
这种特性给你极大的可扩展性。你可以返回任何数据类型,重写任何数据声明或者校验,等等。
这也带来了很大的责任。你必须确保你返回的数据是正确的、格式正确、可被序列化,等等。
在 Response 中使用 jsonable_encoder¶
由于 FastAPI 并未对你返回的 Response 做任何改变,你必须确保你已经准备好响应内容。
例如,如果不首先将 Pydantic 模型转换为 dict,并将所有数据类型(如 datetime、UUID 等)转换为兼容 JSON 的类型,则不能将其放入 JSONResponse 中。
对于这些情况,在将数据传递给响应之前,你可以使用 jsonable_encoder 来转换你的数据:
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
title: str
timestamp: datetime
description: str | None = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
return JSONResponse(content=json_compatible_item_data)
技术细节
你也可以使用 from starlette.responses import JSONResponse。
出于方便,FastAPI 会提供与 starlette.responses 相同的 fastapi.responses 给开发者。但是大多数可用的响应都直接来自 Starlette。
返回自定义 Response¶
上面的例子展示了需要的所有部分,但还不够实用,因为你本可以只是直接返回 item,而 FastAPI 默认帮你把这个 item 放到 JSONResponse 中,又默认将其转换成了 dict 等等。
现在,让我们看看你如何才能返回一个自定义的响应。
假设你想要返回一个 XML 响应。
你可以把你的 XML 内容放到一个字符串中,放到一个 Response 中,然后返回:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
</Header>
<Body>
You'll have to use soap here.
</Body>
</shampoo>
"""
return Response(content=data, media_type="application/xml")
响应模型如何工作¶
当你在路径操作中声明一个 响应模型 - 返回类型 时,FastAPI 会使用它通过 Pydantic 将数据序列化为 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(name="Portal Gun", price=42.0),
Item(name="Plumbus", price=32.0),
]
由于这些工作会在 Rust 侧完成,性能将比在常规 Python 中配合 JSONResponse 类完成要好得多。
当使用 response_model 或返回类型时,FastAPI 不会使用 jsonable_encoder 来转换数据(那样会更慢),也不会使用 JSONResponse 类。
相反,它会采用使用该响应模型(或返回类型)由 Pydantic 生成的 JSON 字节,并直接返回一个具有正确 JSON 媒体类型(application/json)的 Response。
说明¶
当你直接返回 Response 时,它的数据既没有校验,又不会进行转换(序列化),也不会自动生成文档。
但是你仍可以参考 OpenAPI 中的额外响应 给响应编写文档。
在后续的章节中你可以了解到如何使用/声明这些自定义的 Response 的同时还保留自动化的数据转换和文档等。