自定义响应 - HTML、流、文件等¶
默认情况下,FastAPI 会使用 JSONResponse
返回响应。
你可以通过直接返回一个 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
¶
如在 直接返回响应 中所见,你也可以在你的 路径操作 中直接覆盖响应,通过返回它。
上面的例子,返回一个 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
,而不是返回一个 str
中的 HTML。
通过返回调用 generate_html_response()
的结果,你已经在返回一个将覆盖默认 FastAPI 行为的 Response
。
但是,由于你在 response_class
中也传递了 HTMLResponse
,FastAPI 将知道如何在 OpenAPI 和交互式文档中将其记录为带有 text/html
的 HTML:
可用的响应¶
以下是一些可用的响应。
请记住,你可以使用 Response
返回任何其他内容,甚至可以创建自定义子类。
"技术细节"
你也可以使用 from starlette.responses import HTMLResponse
。
FastAPI 提供了与 starlette.responses
相同的 fastapi.responses
,只是为了方便你,开发者。但大多数可用的响应直接来自 Starlette。
Response
¶
主要的 Response
类,所有其他响应都继承自它。
你可以直接返回它。
它接受以下参数:
content
- 一个str
或bytes
。status_code
- 一个int
HTTP 状态码。headers
- 一个字符串的dict
。media_type
- 一个给出媒体类型的str
。例如"text/html"
。
FastAPI(实际上是 Starlette)将自动包含一个 Content-Length 头。它还将根据 media_type 包含一个 Content-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
¶
使用 orjson
的快速替代 JSON 响应,如上所述。
Info
这需要安装 orjson
,例如使用 pip install orjson
。
UJSONResponse
¶
使用 ujson
的替代 JSON 响应。
Info
这需要安装 ujson
,例如使用 pip install ujson
。
Warning
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"}]
Tip
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.tiangolo.com")
或者你可以在 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.tiangolo.com"
如果你这样做,那么你可以直接从你的 路径操作 函数返回 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.dev/"
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
) 的部分进行yield
。因此,它是一个生成器函数,将“生成”工作转移到内部的其他东西上。
通过这种方式,我们可以将其放在
with
块中,并确保在完成后关闭类文件对象。
Tip
请注意,在这里我们使用的是不支持 async
和 await
的标准 open()
,因此我们使用普通的 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"}]
Tip
你仍然可以在 路径操作 中像以前一样覆盖 response_class
。
附加文档¶
你还可以使用 responses
在 OpenAPI 中声明媒体类型和许多其他细节:OpenAPI 中的附加响应。