前言
在FastAPI应用程序中,请求和响应的处理是构建高性能、可维护和强大API的关键部分。主要有如下响应形式:
- 普通字符串 (Plain Text):处理文本信息是构建API的基础。通过FastAPI,您可以轻松地解析和生成普通字符串,确保您的应用程序可以接受和返回清晰的文本数据。
- Json数据 (JSON):作为现代Web API的标准,处理和生成JSON数据是必不可少的一部分。FastAPI提供了内置的JSON解析和生成功能,使您能够直观地处理结构化数据。
- 字节流 (Byte Streams):有时候,您可能需要处理二进制数据或字节流,例如文件上传或图像处理。FastAPI能够轻松地处理这些情况,确保您的应用程序可以处理各种数据流。
- 重定向 (Redirection):在某些情况下,您可能需要将客户端重定向到另一个URL。FastAPI提供了简单而灵活的方式来执行重定向,使您能够实现有效的页面跳转。
普通字符串Plain Text
现在普通字符串的响应很少用了,现在一般都是用json来传递结构化的数据。不常用我们了解下即可。
python
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 响应处理 }
# @Date: 2023/12/05 15:20
import uvicorn
from fastapi import FastAPI, Response
from fastapi.responses import HTMLResponse
app = FastAPI(summary="响应处理")
@app.get("/plain_text_demo")
async def plain_text_demo():
# 默认响应头的 content-type 是 application/json
return "hello fastapi"
@app.get("/plain_text_demo2")
async def plain_text_demo():
return Response(content="hello fastapi 2", media_type="text/plain")
@app.get("/html_demo")
async def html_demo():
html_text = "<h1> hello fastapi </>"
# return Response(content=html_text, media_type="text/html")
return HTMLResponse(content=html_text)
def main():
uvicorn.run(app)
if __name__ == '__main__':
main()
直接返回字符串其实 fastapi 的响应头 content-type 默认会设置成 application/json,但也实现了普通字符串的返回效果。
我们可以使用 fastapi 封装好的响应类来返回就可以修改返回的响应类型。从 fastapi.responses 导入即可。
代码中 HTMLResponse(content=html_text)
返回的是html的文本形式,浏览器可以直接渲染html。之前 前后端不分离的时候后端就会返回数据渲染好的html给浏览器直接展示,也会使用一些jinjia2等模板渲染技术。现在前后端分离,一般都是用json来交互了,数据渲染工作就让前端做了。
Json响应
python
from fastapi.responses import JSONResponse
@app.get("/json_demo1")
async def json_demo():
return {"name": "hui", "age": 18}
@app.get("/json_demo2")
async def json_demo2():
json_content = {
"code": 0,
"message": "ok",
"data": {"name": "hui", "age": 22}
}
return JSONResponse(content=json_content)
默认返回python的字典fastapi返回时会自动转成json类型,也可以使用 JSONResponse 来返回json数据。
内部就是指定了media_type,以及重写了 render 响应数据数据渲染使用内部库json来解析python对象。
FastAPI 也提供了第三方json处理的封装类,比python内置的json库再处理大的json数据时性能更好些。如需使用要先pip 安装
python
pip install ujson orjson
python
from fastapi.responses import JSONResponse, UJSONResponse, ORJSONResponse
@app.get("/ujson_demo")
async def ujson_demo():
ujson_content = {
"code": 0,
"message": "ok",
"data": {"json_type": "ujson"}
}
return UJSONResponse(content=ujson_content)
@app.get("/orjson_demo")
async def orjson_demo():
orjson_content = {
"code": 0,
"message": "ok",
"data": {"json_type": "orjson"}
}
return ORJSONResponse(content=orjson_content)
在实际应用中,建议先使用标准库中的 json
模块,只有在性能成为问题时再考虑迁移到更快的实现,以避免不必要的复杂性。
字节流
python
from fastapi import FastAPI
from fastapi.responses import (
StreamingResponse, FileResponse
)
app = FastAPI(summary="响应处理")
async def fake_streamer():
for i in range(10):
yield b"streaming fake bytes\n"
@app.get("/stream_demo")
async def stream_demo():
return StreamingResponse(fake_streamer())
def iter_video(file_path):
with open(file_path, mode="rb") as file:
yield from file
@app.get("/stream_video_demo")
async def stream_video_demo():
video_path = "res/demo.mp4"
return StreamingResponse(iter_video(video_path), media_type="video/mp4")
fake_streamer 、iter_video 函数是一个异步|同步生成器,用于造一些流式数据。FastAPI 中可以直接使用StreamingResponse 来处理流数据。
文件响应处理 FileResponse
异步传输文件作为响应。
-
path
要流式传输的文件的文件路径。 -
headers
任何自定义响应头,传入字典类型。 -
media_type
给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。 -
filename
如果给出,它将包含在响应的Content-Disposition
中。 -
content_disposition_type
指定处理方式,默认attachment
python
from fastapi.responses import (
StreamingResponse, FileResponse
)
@app.get("/file_demo")
async def file_demo():
video_path = "res/demo.mp4"
return FileResponse(
path=video_path,
filename="test.mp4",
content_disposition_type="inline"
)
Content-Disposition
头字段的常见用途是在响应中包含附件,并提供一个建议的文件名供用户保存。这通常用于下载文件,以确保浏览器将响应体作为文件而不是在浏览器中直接显示。
以下是 Content-Disposition
头的一般形式:
css
Content-Disposition: <type>; filename=<filename>
-
<type>
指定处理方式,例如 "inline"(直接在浏览器中显示)或 "attachment"(提示用户下载文件)。 -
filename=<filename>
提供建议的文件名。
这里使用的是 inline 故而再浏览器中直接显示,如果是 attachment 则会自动下载这个视频文件。
重定向
python
from fastapi.responses import RedirectResponse
@app.get("/redirect_demo")
async def redirect_demo():
return RedirectResponse(url="https://juejin.cn/user/817692384431470")
python
from fastapi.responses import RedirectResponse
@app.get("/redirect_demo")
async def redirect_demo():
return RedirectResponse(url="https://juejin.cn/user/817692384431470", status_code=301)
status_code
指定状态码
-
默认为 307 Temporary Redirect 临时重定向
-
301 永久重定向
Pydantic 指定响应模型
FastAPI 处理响应的时候可以指定pydantic的响应模型,不但可以提高接口的可读性,还可以做到接口响应校验,只有在定义路由的时候指定 response_model 再生成在线的api 文档才会有响应参数的说明。
ini
from pydantic import BaseModel, Field
from fastapi import FastAPI
app = FastAPI(summary="响应处理")
class UserModel(BaseModel):
username: str = Field(description="用户名")
age: int = Field(description="姓名")
hobby: Optional[str] = Field(default="", description="爱好")
@app.get("/pydantic_model_demo", response_model=UserModel)
async def pydantic_model_demo():
user_model = UserModel(
username="hui",
age=18,
hobby="吃饭 睡觉 打游戏"
)
user_info = dict(
username="hui",
hobby="吃饭 睡觉 打游戏"
)
return user_model
# return user_info
也可以使用字典返回,会自动转成pydantic_model 然后校验,如下是校验不通过案例
python
@app.get("/pydantic_model_demo", response_model=UserModel)
async def pydantic_model_demo():
# user_model = UserModel(
# username="hui",
# age=18,
# hobby="吃饭 睡觉 打游戏"
# )
user_info = dict(
username="hui",
hobby="吃饭 睡觉 打游戏"
)
# return user_model
return user_info
由于 username、age是必响应的参数、hobby是可选,字典中没有设置age的值,所以会抛一个 ResponseValidationError 响应校验错误的异常。但一般都推荐使用 model 来代替字典来处理响应以及传递参数,虽然代码量加多了,但这样代码可读性更高,也更好维护。
pydantic model也可以像字典一样嵌套,可以灵活的组织数据结构。
python
class UserOut(BaseModel):
code: int = Field(default=0, description="响应码")
message: str = Field(default="success", description="响应提示信息")
data: UserModel = Field(description="响应数据")
@app.get("/pydantic_model_demo2", response_model=UserOut, summary="嵌套model")
async def pydantic_model_demo():
user_model = UserModel(
username="hui",
age=18,
hobby="吃饭 睡觉 打游戏"
)
return UserOut(data=user_model)
这里为了展示案例,故而没有做太多的封装,后面可以统一封装下响应模型,这样代码就不会太冗余了。
源代码
Github:github.com/HuiDBK/Fast...