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可以实现非常多且实用的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日一题(小白)模拟娱乐篇33

首先&#xff0c;理解题意是十分重要的&#xff0c;我们是要求最短路径&#xff0c;这道题可以用dfs&#xff0c;但是题目给出的数据是有规律的&#xff0c;我们可以尝试模拟的过程使用简单的方法做出来。每隔w数字就会向下转向&#xff0c;就比如题目上示例的w6&#xff0c;无…

哈希封装unordered_map和unordered_set的模拟实现

文章目录 &#xff08;一&#xff09;认识unordered_map和unordered_set&#xff08;二&#xff09;模拟实现unordered_map和unordered_set2.1 实现出复用哈希表的框架2.2 迭代器iterator的实现思路分析2.3 unordered_map支持[] &#xff08;三&#xff09;结束语 &#xff08;…

Java学习-Java基础

1.重写与重载的区别 重写发生在父子类之间,重载发生在同类之间构造方法不能重写,只能重载重写的方法返回值,参数列表,方法名必须相同重载的方法名相同,参数列表必须不同重写的方法的访问权限不能比父类方法的访问权限更低 2.接口和抽象类的区别 接口是interface,抽象类是abs…

BG开发者日志0427:故事的起点

1、4月26日晚上&#xff0c;BG项目的gameplay部分开发完毕&#xff0c;后续是细节以及试玩版优化。 开发重心转移到story部分&#xff0c;目前刚开始&#xff0c; 确切地说以前是长期搁置状态&#xff0c;因为过去的四个月中gameplay部分优先开发。 --- 2、BG这个项目的起点…

头歌实训之游标触发器

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C语言的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

【深度学习】多头注意力机制的实现|pytorch

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a;【深度学习】注意力机制| 基于“上下文”进行编码,用更聪明的矩阵乘法替代笨重的全连接每日一言&#x1f33c;: 路漫漫其修远兮&#xff0c;吾…

java16

1.API续集 可以导入别人写好的clone的jar包 注意&#xff1a;方法要有调用者&#xff0c;如果调用者是null就会报错 2.如何导入别人写好的jar包 复制jar包然后粘贴在lib里面&#xff0c;然后右键点击jar包再点击下面的add 3.关于打印java中的引用数据类型

PostgreSQL的扩展 credcheck

PostgreSQL的扩展 credcheck credcheck 是 PostgreSQL 的一个安全扩展&#xff0c;专门用于强制实施密码策略和凭证检查&#xff0c;特别适合需要符合安全合规要求的数据库环境。 一、扩展概述 1. 主要功能 强制密码复杂度要求防止使用常见弱密码密码过期策略实施密码重复使…

MyBatis中的@Param注解-如何传入多个不同类型的参数

mybatis中参数识别规则 默认情况下,MyBatis 会按照参数位置自动分配名称:param1, param2, param3, ...或者 arg0, arg1。 // Mapper 接口方法 User getUserByIdAndName(Integer id, String name); 以上接口在XML中只能通过param1或者arg0这样的方式来引用,可读性差。 &l…

DIFY教程第一集:安装Dify配置环境

一、Dify的介绍 https://dify.ai/ Dify 是一款创新的智能生活助手应用&#xff0c;旨在为您提供便捷、高效的服务。通过人工智能技术&#xff0c; Dify 可以实现语音 助手、智能家居控制、日程管理等功能&#xff0c;助您轻松应对生活琐事&#xff0c;享受智慧生活。简约的…

5、Rag基础:RAG 专题

RAG 简介 什么是检索增强生成? 检索增强生成(RAG)是指对大型语言模型输出进行优化,使其能够在生成响应之前引用训练数据来源之外的权威知识库。大型语言模型(LLM)用海量数据进行训练,使用数十亿个参数为回答问题、翻译语言和完成句子等任务生成原始输出。在 LLM 本就强…

GAMES202-高质量实时渲染(homework1)

目录 Homework1shadow MapPCF(Percentage Closer Filter)PCSS(Percentage Closer Soft Shadow) GitHub主页&#xff1a;https://github.com/sdpyy1 作业实现:https://github.com/sdpyy1/CppLearn/tree/main/games202 Homework1 shadow Map 首先需要完成MVP矩阵的构造&#xf…

JDK(Ubuntu 18.04.6 LTS)安装笔记

一、前言 本文与【MySQL 8&#xff08;Ubuntu 18.04.6 LTS&#xff09;安装笔记】同批次&#xff1a;先搭建数据库&#xff0c;再安装JDK&#xff0c;后面肯定就是部署Web应用&#xff1a;典型的单机部署。“麻雀虽小五脏俱全”&#xff0c;善始善终&#xff0c;还是记下来吧。…

软件测试之接口测试常见面试题

一、什么是(软件)接口测试? 接口测试&#xff1a;是测试系统组件间接口的一种测试方法 接口测试的重点&#xff1a;检查数据的交换&#xff0c;数据传递的正确性&#xff0c;以及接口间的逻辑依赖关系 接口测试的意义&#xff1a;在较早期开展&#xff0c;在软件开发的同时…

Lua 第11部分 小插曲:出现频率最高的单词

在本章中&#xff0c;我们要开发一个读取并输出一段文本中出现频率最高的单词的程序。像之前的小插曲一样&#xff0c;本章的程序也十分简单但是也使用了诸如迭代器和匿名函数这样的高级特性。 该程序的主要数据结构是一个记录文本中出现的每一个单词及其出现次数之间关系的表。…

软件项目进度管理活动详解

目录 1. 活动定义&#xff08;Activity Definition&#xff09; 2. 活动排序&#xff08;Activity Sequencing&#xff09; 3. 活动资源估算&#xff08;Activity Resource Estimating&#xff09; 4. 活动历时估算&#xff08;Activity Duration Estimating&#xff09; …

docker 国内源和常用命令

Ubuntu | Docker Docs 参考docker官方安装docker # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt…

身份与访问管理(IAM):零信任架构下的认证授权技术与实战

身份与访问管理&#xff08;IAM&#xff09;&#xff1a;零信任架构下的认证授权技术与实战 在网络安全防御体系中&#xff0c;身份与访问管理&#xff08;Identity and Access Management, IAM&#xff09;是守护数字资产的“数字门禁系统”。随着远程办公和多云架构的普及&a…

Maven进阶知识

一、Maven 坐标 &#xff08;一&#xff09;概念 在 Maven 中坐标是构件的唯一标识&#xff0c;其元素包括 groupId、artifactId、version、packaging、classifier。其中 groupId、artifactId、version 是必定义项&#xff0c;packaging 默认为 jar。 &#xff08;二&#x…

网络原理 ——TCP 协议

TCP 报文结构 TCP 头部 20字节&#xff08;无选项&#xff09;&#xff0c;关键字段&#xff1a; 字段长度&#xff08;bit&#xff09;说明源端口16发送方端口目的端口16接收方端口序列号&#xff08;seq&#xff09;32数据字节的编号确认号&#xff08;ack&#xff09;32期…