自定义响应 - HTML、流式、文件等¶
默认情况下,FastAPI 将使用 JSONResponse
返回响应。
你可以通过直接返回一个 Response
来覆盖它,正如 直接返回响应 中所示。
但是,如果你直接返回一个 Response
(或任何子类,如 JSONResponse
),数据将不会自动转换(即使你声明了 response_model
),并且文档也不会自动生成(例如,将特定的“媒体类型”包含在 HTTP 头 Content-Type
中作为生成的 OpenAPI 的一部分)。
但是你也可以在**路径操作装饰器**中使用 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 响应,如上所述。
信息
这需要安装 orjson
,例如使用 pip install orjson
。
UJSONResponse
¶
使用 ujson
的替代 JSON 响应。
信息
这需要安装 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
块中,从而确保在完成后关闭类文件对象。
提示
请注意,由于我们这里使用的是不支持 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"}]
提示
你仍然可以像以前一样在**路径操作**中覆盖 response_class
。
补充文档¶
你还可以使用 responses
在 OpenAPI 中声明媒体类型和许多其他详细信息:OpenAPI 中的附加响应。