Iris MVC默认不支持流式响应,因其设计为“一次写入、整体返回”,与chunked传输、持续写入本质冲突;必须绕过MVC自动响应机制,手动操作ResponseWriter并禁用缓冲、显式Flush。

不能直接用 ctx.JSON() 或返回值自动序列化实现流式响应,必须手动控制 ResponseWriter 并禁用默认缓冲。
为什么 Iris MVC 默认不支持流式响应
Iris 的 MVC 层设计为“一次写入、整体返回”:控制器方法返回值(或 ctx.JSON()/ctx.Text())会触发框架内部的完整响应封装,包括自动设置 Content-Length、启用 gzip 缓冲、关闭连接等行为。这与流式响应(chunked transfer encoding、持续写入、保持连接)本质冲突。
常见错误现象:ctx.JSON() 调用多次 → 只有第一次生效,后续被忽略;ctx.ResponseWriter().Write() 写入后立刻结束 → 客户端收不到分块数据;没禁用缓冲 → 所有内容攒到末尾才发出去。
- 流式响应必须绕过 MVC 的自动响应机制,直接操作底层
http.ResponseWriter - 控制器方法不能返回
mvc.Result(如mvc.Response),否则框架会接管并覆盖你的写入 - 必须显式调用
ctx.ResponseWriter().Flush(),否则数据卡在 Go HTTP 底层缓冲区
如何在 Controller 方法中正确写入流式数据
控制器方法签名需改为 func(c *MyController) Stream(ctx iris.Context),不返回任何值,并全程手动管理响应流。
- 先调用
ctx.Header("Content-Type", "text/event-stream")或"application/json-seq"等适合流的类型 - 禁用默认缓冲:
ctx.ResponseWriter().DisableHeaderWrite(true)(Iris v12.2.5+ 支持) - 手动设置
Transfer-Encoding: chunked(可选,现代客户端通常能自动识别) - 每次写入后必须调用
ctx.ResponseWriter().Flush(),否则数据不会真正发出 - 注意处理客户端断连:
if !ctx.ResponseWriter().WroteHeader() { return }可提前退出
示例(SSE 流):
func (c *StreamController) Stream(ctx iris.Context) {
ctx.Header("Content-Type", "text/event-stream")
ctx.Header("Cache-Control", "no-cache")
ctx.Header("Connection", "keep-alive")
ctx.ResponseWriter().DisableHeaderWrite(true)
<pre class="brush:php;toolbar:false;">for i := 0; i < 10; i++ {
if ctx.IsStopped() {
return
}
_, _ = ctx.WriteString(fmt.Sprintf("data: %s\n\n", strconv.Itoa(i)))
ctx.ResponseWriter().Flush()
time.Sleep(1 * time.Second)
}}
BeforeActivation 中注册路径时必须显式指定方法名
Iris MVC 不会自动把 Stream 当作 HTTP 方法(它只认 Get/Post 等标准名),所以必须用 BeforeActivation 显式绑定:
func (c *StreamController) BeforeActivation(b mvc.BeforeActivation) {
b.Handle("GET", "/stream", "Stream")
}
如果漏掉这步,/stream 路径根本不会被路由到该方法,而是 404 或 fallback 到 Get()(如果存在)。
另外注意:Stream 方法里不能再调用 ctx.Next() 或依赖任何中间件的响应后钩子(如日志记录体内容),因为流式响应一旦开始写入,就无法再修改状态码或头部——这些必须在第一次 Write 前完成。
容易被忽略的连接生命周期问题
流式响应长期占用连接,Iris 默认的空闲超时(IdleTimeout)和读写超时(ReadTimeout/WriteTimeout)会直接中断流。必须在 app.Run() 前显式调整:
app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler, iris.WithIdleTimeout(5*time.Minute))- 若用反向代理(Nginx),还需配置
proxy_buffering off和proxy_read_timeout 300 - 客户端断连后,
ctx.IsStopped()可能延迟返回 true,建议加select+ctx.Request().Context().Done()双重判断
最麻烦的一点:Iris 的 MVC 控制器实例默认是单例(Singleton),多个并发流请求会共享同一结构体字段 —— 如果你在 Stream 方法里用了控制器字段存状态(比如计数器),必须加锁或改用请求级局部变量。


















