自定义响应 - HTML、流、文件等¶
默认情况下,FastAPI 会使用 JSONResponse 返回响应。
你可以通过直接返回一个 Response 来覆盖它,如 直接返回 Response 中所示。
但是,如果你直接返回一个 Response(或任何子类,如 JSONResponse),数据将不会被自动转换(即使你声明了 response_model),并且文档也不会自动生成(例如,在生成的 OpenAPI 中包含 HTTP 标头 Content-Type 中的特定“媒体类型”)。
但是,你也可以在路径操作装饰器中使用 response_class 参数来声明你想要使用的 Response(例如,任何 Response 子类)。
从你的路径操作函数返回的内容将被放入该 Response 中。
如果该 Response 具有 JSON 媒体类型(application/json),例如 JSONResponse 和 UJSONResponse,那么你返回的数据将根据你在路径操作装饰器中声明的 Pydantic response_model 自动转换(和过滤)。
注意
如果你使用的响应类没有媒体类型,FastAPI 将假定你的响应没有内容,因此它不会在其生成的 OpenAPI 文档中记录响应格式。
使用 ORJSONResponse¶
例如,如果你追求极致性能,可以安装并使用 orjson 并将响应设置为 ORJSONResponse。
导入你想要使用的 Response 类(子类),并在路径操作装饰器中声明它。
对于大型响应,直接返回 Response 比返回字典快得多。
这是因为默认情况下,FastAPI 会检查其中的每个项目,并确保它们可以被序列化为 JSON,使用教程中解释的相同的 JSON 兼容编码器。这就是允许你返回任意对象(例如数据库模型)的原因。
但是,如果你确定你返回的内容是JSON 可序列化的,你可以直接将其传递给响应类,从而避免 FastAPI 在将你的返回内容传递给响应类之前通过 jsonable_encoder 进行额外开销。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI()
@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
return ORJSONResponse([{"item_id": "Foo"}])
信息
response_class 参数还将用于定义响应的“媒体类型”。
在这种情况下,HTTP 标头 Content-Type 将被设置为 application/json。
并且它将在 OpenAPI 中被记录为如此。
提示
ORJSONResponse 仅在 FastAPI 中可用,在 Starlette 中不可用。
HTML 响应¶
要直接从 FastAPI 返回 HTML 响应,请使用 HTMLResponse。
- 导入
HTMLResponse。 - 将
HTMLResponse作为路径操作装饰器的response_class参数传递。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
信息
response_class 参数还将用于定义响应的“媒体类型”。
在这种情况下,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>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
警告
由你的路径操作函数直接返回的 Response 将不会在 OpenAPI 中被记录(例如,Content-Type 不会被记录),并且不会出现在自动交互式文档中。
信息
当然,实际的 Content-Type 标头、状态码等将来自你返回的 Response 对象。
在 OpenAPI 中文档化并覆盖 Response¶
如果你想在函数内部覆盖响应,但同时在 OpenAPI 中记录“媒体类型”,你可以同时使用 response_class 参数并返回一个 Response 对象。
然后 response_class 将仅用于记录 OpenAPI 的路径操作,而你的 Response 将按原样使用。
直接返回 HTMLResponse¶
例如,它可以是这样的
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
def generate_html_response():
html_content = """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()
在此示例中,generate_html_response() 函数已经生成并返回了一个 Response,而不是将 HTML 作为 str 返回。
通过返回调用 generate_html_response() 的结果,你实际上正在返回一个 Response,它将覆盖默认的 FastAPI 行为。
但由于你也已经将 HTMLResponse 传递给了 response_class,FastAPI 将知道如何在 OpenAPI 和交互式文档中将其记录为带有 text/html 的 HTML。

可用的响应¶
以下是一些可用的响应。
请记住,你可以使用 Response 来返回任何其他内容,甚至创建自定义子类。
技术细节
你也可以使用 from starlette.responses import HTMLResponse。
FastAPI 提供与 fastapi.responses 相同的 starlette.responses 只是为了方便开发者。但大多数可用的响应直接来自 Starlette。
Response¶
主要的 Response 类,所有其他响应都继承自它。
你可以直接返回它。
它接受以下参数
content-str或bytes。status_code- 一个intHTTP 状态码。headers- 一个dict,值为字符串。media_type- 一个str,表示媒体类型。例如"text/html"。
FastAPI(实际上是 Starlette)会自动包含 Content-Length 标头。它还将包含一个 Content-Type 标头,基于 media_type 并为文本类型添加字符集。
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")
HTMLResponse¶
接受一些文本或字节,并如上文所述返回一个 HTML 响应。
PlainTextResponse¶
接受一些文本或字节,并返回一个纯文本响应。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
JSONResponse¶
接受一些数据并返回一个 application/json 编码的响应。
正如你上面看到的,这是 FastAPI 中默认使用的响应。
ORJSONResponse¶
一个快速的替代 JSON 响应,使用 orjson,如你上面看到的。
信息
这需要安装 orjson,例如使用 pip install orjson。
UJSONResponse¶
一个替代 JSON 响应,使用 ujson。
信息
这需要安装 ujson,例如使用 pip install ujson。
警告
ujson 在处理某些边缘情况时不如 Python 的内置实现那么谨慎。
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
提示
ORJSONResponse 可能是更快的替代方案。
RedirectResponse¶
返回一个 HTTP 重定向。默认使用 307 状态码(临时重定向)。
你可以直接返回 RedirectResponse
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/typer")
async def redirect_typer():
return RedirectResponse("https://typer.fastapi.org.cn")
或者你可以在 response_class 参数中使用它
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
return "https://fastapi.org.cn"
如果你这样做,那么你可以直接从你的路径操作函数返回 URL。
在这种情况下,使用的 status_code 将是 RedirectResponse 的默认值,即 307。
你还可以将 status_code 参数与 response_class 参数结合使用
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
return "https://docs.pydantic.org.cn/"
StreamingResponse¶
接受一个异步生成器或一个普通的生成器/迭代器,并流式传输响应体。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
使用 StreamingResponse 处理类文件对象¶
如果你有一个 类文件 对象(例如 open() 返回的对象),你可以创建一个生成器函数来迭代该类文件对象。
这样,你就不必先将所有内容读入内存,然后可以将该生成器函数传递给 StreamingResponse 并返回它。
这包括许多与云存储、视频处理等交互的库。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
def main():
def iterfile(): # (1)
with open(some_file_path, mode="rb") as file_like: # (2)
yield from file_like # (3)
return StreamingResponse(iterfile(), media_type="video/mp4")
- 这是生成器函数。它之所以是“生成器函数”,是因为它内部包含
yield语句。 - 通过使用
with块,我们确保在生成器函数完成后关闭类文件对象。因此,在完成发送响应之后。 -
这个
yield from告诉函数迭代名为file_like的内容。然后,对于迭代的每个部分,将该部分作为来自此生成器函数(iterfile)的内容产生。所以,它是一个将“生成”工作内部转移给其他东西的生成器函数。
通过这种方式,我们可以将其放入
with块中,从而确保在完成后关闭类文件对象。
提示
请注意,在这里我们使用的是标准 open(),它不支持 async 和 await,所以我们使用普通的 def 来声明路径操作。
FileResponse¶
异步流式传输文件作为响应。
实例化时接受的参数与响应类型不同
path- 要流式传输的文件的文件路径。headers- 要包含的任何自定义标头,作为字典。media_type- 一个字符串,表示媒体类型。如果未设置,将使用文件名或路径来推断媒体类型。filename- 如果设置,将在响应Content-Disposition中包含此项。
文件响应将包含适当的 Content-Length、Last-Modified 和 ETag 标头。
from 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)
你也可以使用 response_class 参数
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/", response_class=FileResponse)
async def main():
return some_file_path
在这种情况下,你可以直接从你的路径操作函数返回文件路径。
自定义响应类¶
你可以创建自己的自定义响应类,继承自 Response 并使用它。
例如,假设你想使用 orjson,但具有包含的 ORJSONResponse 类未使用的某些自定义设置。
假设你想让它返回带缩进和格式化的 JSON,因此你想使用 orjson 选项 orjson.OPT_INDENT_2。
你可以创建一个 CustomORJSONResponse。你需要做的主要事情是创建一个 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, option=orjson.OPT_INDENT_2)
@app.get("/", response_class=CustomORJSONResponse)
async def main():
return {"message": "Hello World"}
现在,而不是返回
{"message": "Hello World"}
…这个响应将返回
{
"message": "Hello World"
}
当然,你可能会发现比格式化 JSON 更好的利用方式。😉
默认响应类¶
在创建 FastAPI 类实例或 APIRouter 时,你可以指定默认使用的响应类。
定义此参数是 default_response_class。
在下面的示例中,FastAPI 将默认在所有路径操作中使用 ORJSONResponse,而不是 JSONResponse。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
提示
你仍然可以像以前一样在路径操作中覆盖 response_class。
附加文档¶
你还可以使用 responses 在 OpenAPI 中声明媒体类型和许多其他详细信息:OpenAPI 中的附加响应。