当前位置: 首页 > news >正文

FastAPI系列06:FastAPI响应(Response)

FastAPI响应(Response)

    • 1、Response入门
    • 2、Response基本操作
      • 设置响应体(返回数据)
      • 设置状态码
      • 设置响应头
      • 设置 Cookies
    • 3、响应模型 response_model
    • 4、响应类型 response_class
      • Response派生类
      • 自定义response_class


在“FastAPI系列05:FastAPI请求(Request)”一节中我们详细了解了FastAPI程序对请求参数的校验和处理,在程序中这些处理后的数据将被送入业务逻辑单元,进行进一步的处理,最终向客户端返回HTTP响应。本节我们通过FastAPI实现大文件断点续传等示例详细讨论FastAPI向客户端返回HTTP响应的内容。

1、Response入门

在HTTP协议中,HTTP响应报文主要由三部分组成:

  1. 状态行 (Status Line)
    状态行包含: HTTP版本号(如HTTP/1.1、HTTP/2。)、状态码(- 如 200、404、500,代表服务器对请求的处理结果。)、状态描述( 如 OK、Not Found、Internal Server Error,是简单的人类可读的描述)

  2. 响应头部 (Response Headers)
    一系列 键值对,告诉客户端一些关于响应报文本身或者服务器的信息。常见的响应头字段有:

    • Content-Type: 指定返回内容的类型,比如 text/html; charset=UTF-8
    • Content-Length: 返回内容的长度(单位:字节)
    • Server: 服务器软件的信息
    • Set-Cookie: 设置客户端的 Cookie
    • Cache-Control: 缓存策略
    • Location: 重定向地址(配合 301/302 状态码)
  3. 响应主体 (Response Body)
    主体部分是服务器真正返回给客户端的数据内容。比如:

    • HTML 代码
    • 图片
    • JSON 格式的数据
    • 二进制文件流

一个常见的HTTP响应报文大致如下:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 137
Connection: keep-alive
Server: nginx/1.18.0<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>

在FastAPI中可以使用Response或其派生对象方便地设置状态码、响应头以及响应主体。

2、Response基本操作

设置响应体(返回数据)

在FastAPI中,可以通过直接返回 dict / list(这时FastAPI 自动转成 JSON)、返回 HTML、文件、流式响应或用 Response 或 StreamingResponse 等手动控制来设置响应体。

from fastapi import FastAPIapp = FastAPI()@app.get("/json")
def read_json():return {"message": "Hello, World"}

设置状态码

FastAPI中定义了status枚举,用于在程序中指定响应状态码。

from fastapi import FastAPI, statusapp = FastAPI()@app.post("/create", status_code=status.HTTP_201_CREATED)
def create_item():return {"msg": "Item created"}

设置响应头

可以用 Response 或 JSONResponse 手动设置头部,也可以在路由里加 response_model、response_class等参数。

from fastapi import FastAPI
from fastapi.responses import JSONResponseapp = FastAPI()@app.get("/custom-header")
def custom_header():content = {"message": "Custom header"}headers = {"X-Custom-Header": "FastAPI"}return JSONResponse(content=content, headers=headers)

设置 Cookies

使用 Response.set_cookie() 方法

from fastapi import FastAPI, Responseapp = FastAPI()@app.get("/set-cookie")
def set_cookie(response: Response):response.set_cookie(key="session_id", value="abc123")return {"message": "Cookie set"}

3、响应模型 response_model

在FastAPI中,response_model主要用来声明和验证返回数据格式。FastAPI 会根据定义的 response_model,完成:

  • 自动校验返回的数据是否符合
  • 自动生成 OpenAPI 文档(自动文档超好看)
  • 自动进行数据的序列化(比如只返回你指定的字段)
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class UserOut(BaseModel):id: intname: str@app.get("/user", response_model=UserOut)
def get_user():# 返回一个字典,FastAPI会根据UserOut去校验和生成响应return {"id": 1, "name": "Alice", "password": "123456"}

说明

  • password字段不会返回给前端!因为 UserOut 没有定义 password 字段。

4、响应类型 response_class

在FastAPI中, response_class主要用来指定响应体的类型和格式。FastAPI 会根据response_class控制返回的内容格式,比如 JSON、HTML、纯文本、文件、流式响应等等。

Response派生类

FastAPI提供了JSONResponse、HTMLResponse、PlainTextResponse、FileResponse、StreamingResponse、RedirectResponse等response_class用于定义响应体的类型和格式,它们均派生自 Response 类。

Response
+content
+status_code
+headers
+media_type
+background
JSONResponse
+media_type = "application/json"
+render()
HTMLResponse
+media_type = "text/html"
+render()
PlainTextResponse
+media_type = "text/plain"
+render()
FileResponse
+return files
StreamingResponse
+streaming data
UJSONResponse
+faster JSON serialization

一般情况下,FastAPI默认使用JSONResponse,返回application/json 响应。下面示例中指定了response_class为HTMLResponse以返回 HTML 字符串。

from fastapi import FastAPI
from fastapi.responses import HTMLResponseapp = FastAPI()@app.get("/html", response_class=HTMLResponse)
def get_html():return "<h1>Hello, HTML</h1>"

自定义response_class

我们可以通过以下几步自定义一个 Response 类:

  1. 继承自 fastapi.Response 或 starlette.responses.Response
  2. 重写 render() 方法,告诉 FastAPI:怎么把内容转成字节流(bytes)
  3. 还可以自定义 media_type(Content-Type)

自定义一个返回 .csv 文件的 Response

from fastapi import FastAPI
from starlette.responses import Responseclass CSVResponse(Response):media_type = "text/csv"def render(self, content: str) -> bytes:# 直接把字符串编码成bytes返回return content.encode("utf-8")app = FastAPI()@app.get("/csv", response_class=CSVResponse)
def get_csv():csv_content = "id,name\n1,Alice\n2,Bob"return csv_content

自定义一个加密后的 JSON Response

import json
from fastapi import FastAPI
from starlette.responses import Response
import base64class EncryptedJSONResponse(Response):media_type = "application/json"def render(self, content: dict) -> bytes:json_data = json.dumps(content)# 简单加密:base64编码(真实项目要用AES/自定义加密)encrypted = base64.b64encode(json_data.encode("utf-8"))return encryptedapp = FastAPI()@app.get("/encrypted", response_class=EncryptedJSONResponse)
def get_encrypted():return {"msg": "secret data"}

自定义一个超大文件分块下载的 Response

from starlette.responses import StreamingResponseclass LargeFileResponse(StreamingResponse):def __init__(self, file_path: str, chunk_size: int = 1024 * 1024):generator = self.file_chunk_generator(file_path, chunk_size)super().__init__(generator, media_type="application/octet-stream")self.headers["Content-Disposition"] = f"attachment; filename={file_path.split('/')[-1]}"@staticmethoddef file_chunk_generator(file_path: str, chunk_size: int):with open(file_path, mode="rb") as file:while chunk := file.read(chunk_size):yield chunk@app.get("/download")
def download_big_file():return LargeFileResponse("bigfile.zip")

实现断点续传(支持 Range )的 StreamingResponse
HTTP协议有个标准:Range 请求头。客户端可以在请求头里加Range: bytes=1000-,意思是:“我只要从第1000个字节开始的数据,后面的。”
这样可以实现大文件断点续传或音频、视频流式播放(在线播放时,只请求一部分)。

from fastapi import FastAPI, Request, HTTPException
from starlette.responses import StreamingResponse
import osapp = FastAPI()# 分块读取文件
def range_file_reader(file_path: str, start: int = 0, end: int = None, chunk_size: int = 1024 * 1024):with open(file_path, "rb") as f:f.seek(start)remaining = (end - start + 1) if end else Nonewhile True:read_size = chunk_size if not remaining else min(remaining, chunk_size)data = f.read(read_size)if not data:breakyield dataif remaining:remaining -= len(data)if remaining <= 0:break@app.get("/download")
async def download_file(request: Request):file_path = "bigfile.zip"  # 换成你的大文件路径if not os.path.exists(file_path):raise HTTPException(status_code=404, detail="File not found")file_size = os.path.getsize(file_path)range_header = request.headers.get("range")if range_header:# 解析 range头bytes_unit, byte_range = range_header.split("=")start_str, end_str = byte_range.split("-")start = int(start_str) if start_str else 0end = int(end_str) if end_str else file_size - 1if start >= file_size:raise HTTPException(status_code=416, detail="Range Not Satisfiable")content_length = end - start + 1headers = {"Content-Range": f"bytes {start}-{end}/{file_size}","Accept-Ranges": "bytes","Content-Length": str(content_length),"Content-Type": "application/octet-stream","Content-Disposition": f"attachment; filename={os.path.basename(file_path)}",}return StreamingResponse(range_file_reader(file_path, start, end),status_code=206,  # Partial Contentheaders=headers,)# 没有Range头,普通全量返回headers = {"Content-Length": str(file_size),"Content-Type": "application/octet-stream","Content-Disposition": f"attachment; filename={os.path.basename(file_path)}",}return StreamingResponse(range_file_reader(file_path),status_code=200,headers=headers,)

在此基础上,还可以:

  • 支持多段 Range(比如同时请求0-100, 200-300),但是这个场景很少,比较复杂
  • 限制最大单次传输大小(保护服务器)
  • 支持 gzip 压缩返回(如果是文本文件)
  • 加上异步读取(aiofiles)提升 IO 性能

总之,通过自定义response_class可以实现非常多且实用的功能。

相关文章:

  • 信号量函数
  • 计算机网络全栈精讲:从 TCP/UDP 原理到 Socket 编程与 HTTP 协议实战(含代码实现)
  • Matlab 报错:尝试将 SCRIPT vl_sift 作为函数执行:
  • Linux 【杂谈】基础知识(一)
  • 双目RealSense系统配置rs_camera.launch----实现D435i自制rosbag数据集到离线场景的slam建图
  • 贪心算法-860.柠檬水找零-力扣(LeetCode)
  • Linux系统编程:信号量Semaphore详解 (IPC)
  • vue3中ref在js中为什么需要.value才能获取/修改值?
  • C++:STL——list
  • 可以隐藏列的表格
  • 单片机 + 图像处理芯片 + TFT彩屏 复选框控件
  • Linux内核中的编译时安全防护:以网络协议栈控制块校验为例
  • 单片机之间的双向通信
  • terraform查看资源建的关联关系
  • 一、linux系统启动过程操作记录
  • 插入排序(直接插入排序、折半插入排序和希尔排序)
  • C++中析构函数
  • log4cpp进阶指南
  • LeetCode 每日一题 2025/4/21-2025/4/27
  • 关于Spark知识点与代码测试的学习总结
  • 物业也能成为居家养老“服务员”,上海多区将开展“物业+养老”试点
  • 北京公园使用指南
  • 淮安四韵·名城新章: 网络名人领略“运河之都”魅力
  • 黄仁勋访华期间表示希望继续与中国合作,贸促会回应
  • 国家发改委:我国能源进口来源多元,企业减少甚至停止自美能源进口对国内能源供应没有影响
  • 我国首个大型通用光谱望远镜JUST在青海启动建设