Gin框架路由中使用中间件时如何多次绑定请求Body?
解决Gin框架中二次绑定请求Body出现EOF错误的问题
问题原因
Gin框架里,请求的Body是单向的io.ReadCloser流。第一次调用ShouldBind时(内部会读取整个Body流),流的读取指针会移到末尾。后续再调用BindJSON尝试读取Body时,已无数据可读,因此返回EOF错误。
解决方案
方案1:缓存请求Body并重置流
在中间件中先读取完整的Body内容缓存,重新设置c.Request.Body,让后续绑定操作能再次读取Body数据。同时可将中间件处理后的参数存入Gin Context的Keys中,供Controller直接使用。
import ( "bytes" "io" "net/http" "github.com/gin-gonic/gin" ) func (a AkSk) Check(c *gin.Context) { // 读取并缓存请求Body bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(http.StatusUnauthorized, apis.NewResult(http.StatusUnauthorized, false, err.Error(), "")) c.Abort() return } // 重置Body流,让后续操作可再次读取 c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 绑定到paramsDealed paramsDealed := map[string]string{} if err := c.ShouldBind(¶msDealed); err != nil { c.JSON(http.StatusUnauthorized, apis.NewResult(http.StatusUnauthorized, false, err.Error(), "")) c.Abort() return } // 将处理后的参数存入Context,供Controller使用 c.Set("paramsDealed", paramsDealed) }
Controller中即可正常执行BindJSON:
func Controller(c *gin.Context) { param := params.Params{} err := c.BindJSON(¶m) if err != nil { c.JSON(http.StatusBadRequest, apis.NewResult(http.StatusBadRequest, false, err.Error(), map[string]interface{}{})) return } // 如需使用中间件处理后的参数,直接从Context获取 // paramsDealed, ok := c.Get("paramsDealed") // if !ok { // // 处理参数获取失败逻辑 // } }
方案2:仅绑定一次,共享参数
如果中间件和Controller需要的参数结构兼容,可在中间件中完成Body读取和绑定,将结果存入Context的Keys中,Controller直接从Keys中获取参数,避免二次绑定操作。
import ( "bytes" "io" "net/http" "encoding/json" "github.com/gin-gonic/gin" ) func (a AkSk) Check(c *gin.Context) { // 读取并缓存请求Body bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(http.StatusUnauthorized, apis.NewResult(http.StatusUnauthorized, false, err.Error(), "")) c.Abort() return } // 绑定到中间件需要的paramsDealed paramsDealed := map[string]string{} if err := json.Unmarshal(bodyBytes, ¶msDealed); err != nil { c.JSON(http.StatusUnauthorized, apis.NewResult(http.StatusUnauthorized, false, err.Error(), "")) c.Abort() return } c.Set("paramsDealed", paramsDealed) // 同时绑定到Controller需要的参数结构 var controllerParam params.Params if err := json.Unmarshal(bodyBytes, &controllerParam); err != nil { c.JSON(http.StatusUnauthorized, apis.NewResult(http.StatusUnauthorized, false, err.Error(), "")) c.Abort() return } c.Set("controllerParam", controllerParam) }
Controller直接获取参数:
func Controller(c *gin.Context) { param, ok := c.Get("controllerParam") if !ok { c.JSON(http.StatusBadRequest, apis.NewResult(http.StatusBadRequest, false, "参数获取失败", map[string]interface{}{})) return } // 类型断言转换为目标结构 realParam, ok := param.(params.Params) if !ok { c.JSON(http.StatusBadRequest, apis.NewResult(http.StatusBadRequest, false, "参数类型错误", map[string]interface{}{})) return } // 执行业务逻辑 }
内容的提问来源于stack exchange,提问作者catFly
相关产品推荐
相关产品推荐

