最常见原因是多次调用 ctx.ReadBody() 导致 body 流已耗尽,应只读一次并存入 ctx.Values();全局限制需用 app.Adapt() 包装 handler 检查 ContentLength;分组限流可用前置中间件手动校验。

为什么 ctx.ReadBody() 会 panic 或读不到数据
最常见原因是请求体超过默认限制(Iris 默认是 32MB),但更隐蔽的问题是:中间件或 handler 里多次调用 ctx.ReadBody(),而 HTTP body 是单次可读流。第一次读完后,后续再读就返回空或 panic。
尤其在鉴权中间件里提前读 body(比如解析 JWT payload),又没做缓存,下游 handler 再读就会失败。这不是 bug,是 Go HTTP 底层行为。
- 别在多个中间件里重复调
ctx.ReadBody()—— 读一次,存到ctx.Values().Set("raw_body", data) - 若必须多次访问,改用
ctx.Request().Body+io.ReadAll后手动重置(不推荐,易出错) - 更稳妥的做法:统一在第一个需要 body 的中间件里读,并把结果塞进
ctx.Values()供后续使用
如何全局限制 POST/PUT 请求体大小
Iris 没有类似 app.MaxRequestBodySize() 这样的快捷方法,得靠 app.Adapt() 注入自定义 http.Handler 来拦截原始请求,在路由匹配前就做限制。
关键点是:必须在 app.Use() 和所有路由注册之前调用,否则限制不生效。
- 用
app.Adapt(func(h http.Handler) http.Handler { ... })包裹原 handler - 在包装函数里检查
r.ContentLength,超限直接http.Error(w, "Request Entity Too Large", http.StatusRequestEntityTooLarge) - 注意:只对
POST/PUT/PATCH等带 body 的方法做限制,GET和HEAD可跳过 - 示例片段:
app.Adapt(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" { if r.ContentLength > 5*1024*1024 { // 5MB http.Error(w, "Request Entity Too Large", http.StatusRequestEntityTooLarge) return } } h.ServeHTTP(w, r) }) })
怎么让 /api/* 路由组单独用更严的限制
全局限制太粗,某些接口(如文件上传)要放宽,而登录接口只需 1KB JSON,就得细化控制。Iris 不支持 per-route 体大小限制,但可以用分组 + 中间件模拟。
思路是:在目标分组的第一个中间件里,手动读取并校验 ContentLength,超限立即终止;同时确保这个中间件注册在分组最前,且不调 ctx.Next()。
- 写一个中间件:
func limitBody(size int64) iris.Handler { ... },内部用ctx.Request().ContentLength判断 - 注册时:
api := app.Party("/api"); api.Use(limitBody(1 - 注意:不能依赖
ctx.ReadBody()来测大小 —— 它会消费 body,应直接看ctx.Request().ContentLength - 如果前端不发
Content-Length(比如用 chunked encoding),这个值是 -1,需额外处理(例如用io.LimitReader包一层ctx.Request().Body)
OPTIONS 预检请求为什么不能被体大小限制误杀
浏览器发 OPTIONS 预检时,通常不带 body,但有些客户端(尤其测试工具)可能附带空 body 或无效 header。若你在中间件里无差别读 body 或校验长度,会导致预检失败,CORS 直接垮掉。
必须在任何涉及 body 操作前,先判断方法:
- 在体限制中间件开头加:
if ctx.Method() == "OPTIONS" { ctx.Next(); return } - 千万别写
ctx.ReadBody()或ctx.Request().Body相关逻辑 —— OPTIONS 不该触发任何业务解析 - 更稳妥的是:显式注册
app.Options("/api/*", func(ctx iris.Context) { ctx.StatusCode(204) }),确保预检一定被接住,不走限制逻辑
真正容易被忽略的不是“怎么设上限”,而是 Content-Length 缺失时的行为和 OPTIONS 的静默放行 —— 这两点不处理好,上线后第一波跨域请求就会挂掉。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











