自定义响应 - 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
并返回它,而不是将 HTML 返回到 str
中。
通过返回调用 generate_html_response()
的结果,您实际上已经返回了一个 Response
,它将覆盖 FastAPI 的默认行为。
但是,由于您也在 response_class
中传递了 HTMLResponse
,FastAPI 将知道如何在 OpenAPI 和交互式文档中将其记录为具有 text/html
的 HTML。
可用响应¶
以下是一些可用的响应。
请记住,您可以使用 Response
返回任何其他内容,甚至创建自定义子类。
"技术细节"
您也可以使用 from starlette.responses import HTMLResponse
。
FastAPI 提供与 fastapi.responses
相同的 starlette.responses
,仅作为开发人员的便利。但大多数可用的响应直接来自 Starlette。
Response
¶
主要的 Response
类,所有其他响应都继承自它。
您可以直接返回它。
它接受以下参数
content
- 一个str
或bytes
。status_code
- 一个int
HTTP 状态码。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
¶
使用 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
)的一部分进行 yield。因此,它是一个生成器函数,它将“生成”工作内部传递给其他东西。
通过以这种方式进行,我们可以将其放入
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 中的其他响应.