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

在Vapor中实现流式响应时如何监听客户端的请求取消事件?

在Vapor中实现流式响应时如何监听客户端的请求取消事件?

嘿,刚好我之前做Vapor流式响应时也碰到过这个需求,和你一样想在客户端断开或取消请求时及时停掉服务器端的无用处理。下面结合你的代码,给你讲讲具体怎么实现:

首先得说,Vapor的Request对象本身就自带了请求取消的跟踪机制,这和你熟悉的Express里监听response.close是同一个逻辑,只是实现方式有点不同。我们可以从两个层面来处理:

  • 一是在数据写入循环里主动检查请求是否被取消
  • 二是把处理流式写入的Task和请求的生命周期绑定,让请求取消时自动终止Task

先看你自定义的encodeResponse方法,我们可以修改里面的Task逻辑,加入取消监听:

public func encodeResponse(for request: Request) async throws -> Response {
    let response = Response(status: .ok)
    let body = Response.Body(stream: { writer in
        let task = Task { [request] in
            do {
                for try await string in self {
                    // 每次写入前先检查客户端是否取消了请求
                    guard !request.isCancelled else {
                        throw CancellationError()
                    }
                    _ = writer.write(.buffer(ByteBuffer(string: "data: \(string)\n\n")))
                    // 也可以用Task.checkCancellation()直接抛出取消错误
                    try Task.checkCancellation()
                }
            } catch is CancellationError {
                // 这里就是客户端取消请求的回调逻辑,你可以在这里做清理、停止后台处理等操作
                print("客户端取消了请求,立即停止处理流程")
            } catch {
                _ = writer.write(.buffer(ByteBuffer(string: "data: \(error.localizedDescription)\n\n")))
            }
            
            // 只有请求没被取消的情况下,才写入完成标记
            if !request.isCancelled {
                _ = writer.write(.buffer(ByteBuffer(string: "data: [DONE]")))
            }
            _ = writer.write(.end)
        }
        // 把Task添加到请求的cancellables中,请求取消时自动取消这个Task
        request.cancellables.add(task)
    })
    response.headers.contentType = HTTPMediaType(type: "text", subType: "event-stream")
    response.body = body
    return response
}

这样修改后,当客户端断开连接或者取消请求时:

  1. request.isCancelled会立刻变为true
  2. 绑定的Task会收到取消信号,Task.checkCancellation()会抛出CancellationError
  3. 我们可以在catch分支里处理取消逻辑,比如停止后台的数据生成、释放资源等

另外,如果你是在streamData(req:)方法里处理数据生成的逻辑(比如从数据库或其他地方拉取数据),也可以直接在那里监听请求的取消:

func streamData(req: Request) async throws -> AsyncStream<String> {
    return AsyncStream { continuation in
        // 把这个continuation的取消和请求绑定
        req.cancellables.add {
            continuation.finish()
            // 这里做数据生成的清理工作
            print("请求取消,停止数据生成")
        }
        
        // 模拟数据生成
        Task {
            for i in 1...10 {
                guard !req.isCancelled else { break }
                try await Task.sleep(for: .seconds(1))
                continuation.yield("Item \(i)")
            }
            continuation.finish()
        }
    }
}

这个思路和你熟悉的Express里response.on("close")完全对应,都是在客户端断开时触发我们的清理逻辑,避免服务器做不必要的计算。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 07:48:04