FastAPI 响应类型完全指南:从 JSON 到流式响应、异常处理与输出模型

【学习记录】FastAPI 响应类型完全指南:从 JSON 到流式响应、异常处理与输出模型

在构建 Web API 时,不仅需要接收参数,还需要灵活控制返回给客户端的响应格式。FastAPI 提供了丰富的响应类型支持,包括 JSON、HTML、纯文本、文件、流式数据、重定向等,还可以通过 response_model 自动验证和序列化输出,并通过 HTTPException 规范错误响应。本文系统讲解 FastAPI 中所有常用的响应类型及其使用场景,提供完整可运行的代码示例,助你构建专业、健壮的 API。


📌 目录

  1. JSONResponse(默认行为)
  2. [HTMLResponse:返回 HTML 页面](#HTMLResponse:返回 HTML 页面)
  3. PlainTextResponse:纯文本响应
  4. FileResponse:文件下载与预览
  5. StreamingResponse:流式传输大文件或实时数据
  6. RedirectResponse:重定向
  7. 在装饰器中指定响应类型(response_class
  8. 直接返回响应对象
  9. [自定义输出模型:response_model 自动验证与文档生成](#自定义输出模型:response_model 自动验证与文档生成)
  10. [异常响应处理:HTTPException 与自定义异常处理器](#异常响应处理:HTTPException 与自定义异常处理器)
  11. 完整可运行示例
  12. 总结与最佳实践

一、JSONResponse(默认行为)

FastAPI 默认使用 JSONResponse 将返回的字典或 Pydantic 模型自动转换为 JSON 格式,并设置 Content-Type: application/json。你无需显式导入,直接返回 Python 字典即可。

python 复制代码
from fastapi import FastAPI

app = FastAPI()

@app.get("/json-default")
def json_default():
    return {"message": "This is automatically converted to JSONResponse"}

也可以显式使用 JSONResponse,以便自定义状态码或其他响应头:

python 复制代码
from fastapi.responses import JSONResponse

@app.get("/json-explicit")
def json_explicit():
    return JSONResponse(content={"message": "Explicit JSONResponse"}, status_code=201)

二、HTMLResponse:返回 HTML 页面

使用 HTMLResponse 可以返回 HTML 内容,浏览器会将其渲染为网页。

python 复制代码
from fastapi.responses import HTMLResponse

@app.get("/html", response_class=HTMLResponse)
def html_response():
    html_content = """
    <html>
        <head><title>FastAPI HTML</title></head>
        <body>
            <h1>Hello, FastAPI!</h1>
            <p>This is an HTML response.</p>
        </body>
    </html>
    """
    return html_content

三、PlainTextResponse:纯文本响应

当需要返回纯文本(如日志、配置内容)时,使用 PlainTextResponse,避免框架自动添加额外格式。

python 复制代码
from fastapi.responses import PlainTextResponse

@app.get("/text", response_class=PlainTextResponse)
def text_response():
    return "This is plain text response."

四、FileResponse:文件下载与预览

FileResponse 可以返回服务器上的文件,自动推断 Content-Type(如 image/jpegapplication/pdf),支持设置下载文件名。

python 复制代码
from fastapi.responses import FileResponse

@app.get("/file")
def file_response():
    # 假设当前目录下存在 example.pdf
    return FileResponse("example.pdf", filename="document.pdf", media_type="application/pdf")
  • filename:建议浏览器下载时的文件名。
  • media_type:可手动指定,不指定则自动推断。

五、StreamingResponse:流式传输大文件或实时数据

当文件很大或需要边生成边发送数据(如实时日志、视频流、AI 生成流)时,使用 StreamingResponse。它接受一个异步生成器或迭代器,逐块发送数据。

python 复制代码
from fastapi.responses import StreamingResponse
import asyncio

async def data_generator():
    for i in range(5):
        yield f"Chunk {i}\n".encode()
        await asyncio.sleep(1)

@app.get("/stream")
async def streaming_response():
    return StreamingResponse(data_generator(), media_type="text/plain")

六、RedirectResponse:重定向

RedirectResponse 用于将客户端重定向到另一个 URL,支持永久(301)或临时(302)重定向。

python 复制代码
from fastapi.responses import RedirectResponse

@app.get("/redirect")
def redirect():
    return RedirectResponse(url="https://fastapi.tiangolo.com", status_code=302)

七、在装饰器中指定响应类型(response_class

无需在函数内部返回响应对象,直接在装饰器中使用 response_class 参数即可指定该端点的默认响应类型。函数体内返回对应格式的内容(字符串、字典等),FastAPI 会自动封装。

python 复制代码
@app.get("/html-decorator", response_class=HTMLResponse)
def html_decorator():
    # 直接返回 HTML 字符串,FastAPI 会用 HTMLResponse 包装
    return "<h1>Title</h1><p>This HTML is from decorator.</p>"

八、直接返回响应对象

在路径操作函数中直接返回 Response 子类的实例,可以完全控制响应内容、状态码和头部。

python 复制代码
@app.get("/return-obj")
def return_response_object():
    return HTMLResponse(content="<h2>Returned as HTMLResponse object</h2>", status_code=200)

九、自定义输出模型:response_model 自动验证与文档生成

通过 response_model 参数指定一个 Pydantic 模型,FastAPI 会:

  • 自动验证返回值是否符合模型定义(类型、必填字段等)
  • 序列化模型实例为 JSON
  • 在 OpenAPI 文档中生成精确的响应 schema
python 复制代码
from pydantic import BaseModel
from typing import List

class Item(BaseModel):
    name: str
    price: float

@app.get("/items/{item_id}", response_model=Item)
def get_item(item_id: int):
    # 实际可能从数据库查询
    return Item(name="Apple", price=1.99)

@app.get("/items", response_model=List[Item])
def list_items():
    return [Item(name="Apple", price=1.99), Item(name="Orange", price=2.49)]

注意 :返回的数据会自动排除模型中未定义的字段(如内部字段 _id),起到输出过滤作用。


十、异常响应处理:HTTPException 与自定义异常处理器

10.1 抛出标准 HTTP 异常

使用 HTTPException 可以返回标准 HTTP 错误码和错误信息,FastAPI 会自动生成对应的 JSON 响应。

python 复制代码
from fastapi import HTTPException

@app.get("/secure/{code}")
def secure_endpoint(code: int):
    if code != 42:
        raise HTTPException(status_code=403, detail="Invalid access code")
    return {"message": "Welcome"}

10.2 自定义异常处理器

可以覆盖默认的异常响应格式,例如统一错误返回格式。

python 复制代码
from fastapi import Request
from fastapi.responses import JSONResponse

@app.exception_handler(403)
async def forbidden_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(status_code=403, content={"error": "Access forbidden", "detail": exc.detail})

十一、完整可运行示例

将上述所有片段整合到一个文件中(main.py),启动服务后即可测试。

python 复制代码
from fastapi import FastAPI, HTTPException
from fastapi.responses import (
    JSONResponse, HTMLResponse, PlainTextResponse,
    FileResponse, StreamingResponse, RedirectResponse
)
from pydantic import BaseModel
from typing import List
import asyncio

app = FastAPI(title="响应类型演示", description="展示各种响应方式")

# 1. JSONResponse(默认)
@app.get("/json-default")
def json_default():
    return {"message": "JSON default"}

# 2. HTMLResponse
@app.get("/html", response_class=HTMLResponse)
def html_response():
    return "<html><body><h1>HTML</h1></body></html>"

# 3. PlainTextResponse
@app.get("/text", response_class=PlainTextResponse)
def text_response():
    return "Plain text"

# 4. FileResponse(需要存在 example.pdf)
@app.get("/file")
def file_response():
    return FileResponse("example.pdf", filename="doc.pdf")

# 5. StreamingResponse
async def stream_gen():
    for i in range(3):
        yield f"data: {i}\n".encode()
        await asyncio.sleep(0.5)

@app.get("/stream")
async def streaming():
    return StreamingResponse(stream_gen(), media_type="text/plain")

# 6. RedirectResponse
@app.get("/redirect")
def redirect():
    return RedirectResponse("https://fastapi.tiangolo.com")

# 7. 装饰器指定响应类型(已由 html 端点展示)

# 8. 直接返回响应对象
@app.get("/return-obj")
def return_obj():
    return HTMLResponse("<h2>Direct Response Object</h2>")

# 9. response_model 自定义输出格式
class Item(BaseModel):
    name: str
    price: float

@app.get("/items/{item_id}", response_model=Item)
def get_item(item_id: int):
    return Item(name="Apple", price=1.99)

@app.get("/items", response_model=List[Item])
def list_items():
    return [Item(name="Apple", price=1.99), Item(name="Orange", price=2.49)]

# 10. HTTPException
@app.get("/secure/{code}")
def secure(code: int):
    if code != 42:
        raise HTTPException(status_code=403, detail="Invalid code")
    return {"message": "OK"}

# 自定义异常处理器(可选)
@app.exception_handler(403)
async def custom_403(request, exc):
    return JSONResponse(status_code=403, content={"detail": "Forbidden custom message"})

启动服务

bash 复制代码
uvicorn main:app --reload

访问测试

  • http://127.0.0.1:8000/json-default
  • http://127.0.0.1:8000/html
  • http://127.0.0.1:8000/text
  • http://127.0.0.1:8000/file(需文件存在)
  • http://127.0.0.1:8000/stream
  • http://127.0.0.1:8000/redirect
  • http://127.0.0.1:8000/return-obj
  • http://127.0.0.1:8000/items/1
  • http://127.0.0.1:8000/items
  • http://127.0.0.1:8000/secure/123(返回 403)

十二、总结与最佳实践

响应类型 适用场景 关键点
JSONResponse REST API 默认格式 直接返回字典或 Pydantic 模型
HTMLResponse 返回网页、富文本内容 可使用 response_class=HTMLResponse
PlainTextResponse 纯文本(日志、配置) 避免额外格式包装
FileResponse 文件下载、图片预览 自动识别 Content-Type
StreamingResponse 大文件、实时流、SSE 使用异步生成器
RedirectResponse 跳转到其他 URL 可设置永久/临时重定向
response_model 输出格式约束、自动文档 推荐用于所有公开 API
HTTPException 统一错误响应 可自定义异常处理器

核心建议

  • ✅ 默认使用 JSONResponse 返回结构化数据。
  • ✅ 为所有公开端点声明 response_model,即使返回字典也建议定义模型。
  • ✅ 对于大文件或流式数据,优先使用 StreamingResponse 避免内存溢出。
  • ✅ 错误处理统一使用 HTTPException,并考虑全局异常处理器。
  • ✅ 利用 response_class 简化装饰器代码,使函数体更简洁。

通过灵活运用 FastAPI 的响应类型,你可以轻松构建出功能丰富、文档完善、健壮可维护的 Web API。

相关推荐
ct9781 小时前
vue-router + Pinia + Vuex
前端·javascript·vue.js
小雨下雨的雨1 小时前
家庭药品管理系统智能过期预警鸿蒙PC Electron框架技术深度解析
前端·javascript·人工智能·华为·electron·鸿蒙·鸿蒙系统
ct9782 小时前
vue2 + vue3差异点
前端·javascript·vue.js
小徐_23332 小时前
程序员每天盯屏 10 小时,我开始认真研究“专业编程屏”这件事
前端
悟空瞎说2 小时前
Git 协作工作流详解:从个人单打独斗到规模化团队协同
前端·git
颜进强2 小时前
20-Spec-Kit Tasks 是怎么把技术方案拆成可执行任务的?
前端·后端·ai编程
程序员鱼皮2 小时前
Cursor 零基础实战教程,夯爆了!带你速通 6 大核心能力
前端·后端·ai编程
颜进强2 小时前
14-Spec-Kit、SDD 和 OpenSpec 到底有什么区别?其实核心思想都一样:先写清楚,再让 AI 干活
前端·后端·ai编程
颜进强2 小时前
16-Spec-Kit 是什么?先从整体流程机制讲起
前端·后端·ai编程