You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断FastAPI/Starlette中StreamingResponse的连接是否已断开?

在FastAPI/Starlette中检测StreamingResponse的客户端断开连接(EventSource场景)

针对EventSource场景下需要感知客户端断开连接的需求,以下是几种实用的实现方式:

1. 捕获生成器迭代时的异常

当客户端主动断开连接,Starlette在尝试向客户端发送数据时会抛出连接相关异常(如ConnectionResetError、BrokenPipeError),同时生成器会收到GeneratorExit信号。只需在生成器内部捕获这些异常,即可触发断开事件处理逻辑。

示例代码:

from fastapi import FastAPI
from starlette.responses import StreamingResponse
import asyncio

app = FastAPI()

async def event_stream():
    try:
        while True:
            # 模拟发送EventStream格式的数据
            yield f"data: {asyncio.get_event_loop().time()}\n\n"
            await asyncio.sleep(1)
    except (ConnectionResetError, BrokenPipeError, GeneratorExit):
        # 客户端断开时执行的逻辑
        print("客户端已断开连接")
        # 替换为你的自定义处理,比如清理资源、通知其他服务等
        cleanup_client_connection()

def cleanup_client_connection():
    # 自定义断开处理逻辑示例
    pass

@app.get("/stream")
async def stream():
    return StreamingResponse(event_stream(), media_type="text/event-stream")

2. 主动检查Request连接状态

通过FastAPI的Request对象,可以调用异步方法is_disconnected()主动检测客户端连接状态。适合发送数据间隔较长的场景,无需等待异常触发就能及时感知断开。

示例代码:

from fastapi import FastAPI, Request
from starlette.responses import StreamingResponse
import asyncio

app = FastAPI()

async def event_stream(request: Request):
    while True:
        # 主动检查连接是否已断开
        if await request.is_disconnected():
            print("客户端已断开连接")
            cleanup_client_connection()
            break
        yield f"data: {asyncio.get_event_loop().time()}\n\n"
        await asyncio.sleep(1)

def cleanup_client_connection():
    pass

@app.get("/stream")
async def stream(request: Request):
    return StreamingResponse(event_stream(request), media_type="text/event-stream")

3. 异步上下文管理器实现优雅清理

使用异步上下文管理器可以将断开后的资源清理逻辑封装起来,无论连接是正常结束还是异常断开,都会自动执行清理操作,代码结构更整洁。

示例代码:

from fastapi import FastAPI, Request
from starlette.responses import StreamingResponse
import asyncio
from contextlib import asynccontextmanager

app = FastAPI()

@asynccontextmanager
async def connection_handler():
    try:
        yield
    finally:
        # 连接终止时统一执行的清理逻辑
        print("客户端连接已终止")
        cleanup_client_connection()

async def event_stream(request: Request):
    async with connection_handler():
        while True:
            if await request.is_disconnected():
                break
            yield f"data: {asyncio.get_event_loop().time()}\n\n"
            await asyncio.sleep(1)

def cleanup_client_connection():
    pass

@app.get("/stream")
async def stream(request: Request):
    return StreamingResponse(event_stream(request), media_type="text/event-stream")

内容的提问来源于stack exchange,提问作者est

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 08:28:15