beego.InsertFilter注册后ctx.Request.Body为空,因Beego默认不缓存原始请求体;需在main()开头设beego.BConfig.CopyRequestBody = true,使Body缓存至ctx.Input.RequestBody。

beego.InsertFilter注册后,ctx.Request.Body 为什么是空的
因为 Beego 默认不缓存原始请求体,ctx.Request.Body 是一个已读完或被丢弃的 io.ReadCloser。直接 io.ReadAll(ctx.Request.Body) 多数情况下返回空字节切片,且后续再读会 panic 或 EOF。
必须在应用启动时启用拷贝机制:beego.BConfig.CopyRequestBody = true(写在 main() 函数开头,beego.Run() 之前)。该配置会让 Beego 在路由前把原始 Body 读入内存并缓存到 ctx.Input.RequestBody 中。
常见错误现象:
- 中间件里
io.ReadAll(ctx.Request.Body)返回[]byte{},但前端明明发了 JSON - 调用
json.Unmarshal失败,报invalid character '' looking for beginning of value - 同一个请求中,中间件读了一次 Body,Controller 再读就失败
中间件中正确读取 JSON Body 的三步操作
启用 CopyRequestBody 后,Body 数据才真正可用。但中间件函数里不能直接用 this.ParseForm()(它只处理表单),也不能依赖自动绑定。
推荐做法是手动解码 ctx.Input.RequestBody:
- 先检查
ctx.Request.Header.Get("Content-Type")是否含application/json - 再用
json.Unmarshal(ctx.Input.RequestBody, &v)解析,**必须检查 error** - 若需复用 Body(比如鉴权后转发给 Controller),不要清空
ctx.Input.RequestBody,Beego 会自动传递
示例片段:
func JsonAuthMiddleware(ctx *context.Context) {
if ctx.Request.Method != "POST" && ctx.Request.Method != "PUT" {
ctx.Next()
return
}
ct := ctx.Request.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
ctx.Abort(400, "Content-Type must be application/json")
return
}
var req map[string]interface{}
if err := json.Unmarshal(ctx.Input.RequestBody, &req); err != nil {
ctx.Abort(400, "invalid json body")
return
}
// 鉴权逻辑,比如检查 req["token"]
ctx.Next()
}
beego.BeforeStatic 阶段无法读 Body 的原因和绕过方式
beego.BeforeStatic 是唯一能拦截 /static/ 类路径的过滤点,但它在 Beego 静态处理器介入前触发——此时 ctx.Input.RequestBody 还未被填充(即使设置了 CopyRequestBody = true),ctx.Request.Body 也已不可读。
这意味着:你无法在 BeforeStatic 中解析 JSON 或表单数据。如果要对静态资源做带参数的权限控制(如 /static/private/:id/file.pdf),只能依赖 URL 路径、Query 参数或 Header。
可行方案:
- 把认证信息放在 Query(如
/static/private/123/file.pdf?token=abc),用ctx.Input.Query("token")提取 - 从
ctx.Request.Header.Get("Authorization")读 token - 用正则从
ctx.Request.URL.Path中提取关键字段,例如匹配/static/private/(\d+)/ - 避免在
BeforeStatic中调用任何涉及 Body 的方法,否则会 panic
中间件里读 Body 后,Controller 还能再读吗
可以,但前提是没手动关闭或重置 ctx.Request.Body。Beego 的 CopyRequestBody = true 机制确保 ctx.Input.RequestBody 是一份独立缓存,Controller 中仍可安全调用 json.Unmarshal(ctx.Input.RequestBody, &v) 或 this.GetString("key")(后者只查 query/form)。
容易被忽略的关键点:
-
ctx.Input.RequestBody是[]byte,不是流;多次Unmarshal没问题,但反复io.Read原始Body会失败 - 若中间件里已解析并修改了
ctx.Input.RequestBody(比如脱敏重写),Controller 读到的就是改后的数据 - GET 请求没有 Body,
ctx.Input.RequestBody恒为空,别在 GET 中尝试解析它











