FastAPI 请求响应处理

前言

在FastAPI应用程序中,请求和响应的处理是构建高性能、可维护和强大API的关键部分。主要有如下响应形式:

  1. 普通字符串 (Plain Text):处理文本信息是构建API的基础。通过FastAPI,您可以轻松地解析和生成普通字符串,确保您的应用程序可以接受和返回清晰的文本数据。
  2. Json数据 (JSON):作为现代Web API的标准,处理和生成JSON数据是必不可少的一部分。FastAPI提供了内置的JSON解析和生成功能,使您能够直观地处理结构化数据。
  3. 字节流 (Byte Streams):有时候,您可能需要处理二进制数据或字节流,例如文件上传或图像处理。FastAPI能够轻松地处理这些情况,确保您的应用程序可以处理各种数据流。
  4. 重定向 (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_streameriter_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...

相关推荐
denghai邓海18 分钟前
红黑树删除之向上调整
python·b+树
学习路上_write26 分钟前
FPGA/Verilog,Quartus环境下if-else语句和case语句RT视图对比/学习记录
单片机·嵌入式硬件·qt·学习·fpga开发·github·硬件工程
封步宇AIGC44 分钟前
量化交易系统开发-实时行情自动化交易-3.4.1.2.A股交易数据
人工智能·python·机器学习·数据挖掘
何曾参静谧44 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices1 小时前
C++如何调用Python脚本
开发语言·c++·python
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
Jam-Young1 小时前
Python的装饰器
开发语言·python
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django
hlsd#2 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端