Gin 如果`request body` 绑定在中间件中,则c.Request.Body 变为0

rlu*_*isr 1 middleware go go-gin

我的 API 服务器具有从请求标头获取令牌的中间件。如果访问正确,则执行下一个功能。

但是请求去了中间件,然后去了下一个函数,c.Request.Body变成了0

中间件

func getUserIdFromBody(c *gin.Context) (int) {
    var jsonBody User

    length, _ := strconv.Atoi(c.Request.Header.Get("Content-Length"))
    body := make([]byte, length)
    length, _ = c.Request.Body.Read(body)
    json.Unmarshal(body[:length], &jsonBody)

    return jsonBody.Id
}

func CheckToken() (gin.HandlerFunc) {
    return func(c *gin.Context) {
        var userId int

        config := model.NewConfig()

        reqToken := c.Request.Header.Get("token")

        _, resBool := c.GetQuery("user_id")
        if resBool == false {
            userId = getUserIdFromBody(c)
        } else {
            userIdStr := c.Query("user_id")
            userId, _ = strconv.Atoi(userIdStr)
        }
    ...
        if ok {
            c.Nex()
            return
        }
}
Run Code Online (Sandbox Code Playgroud)

下一个功能

func bindOneDay(c *gin.Context) (model.Oneday, error) {
    var oneday model.Oneday

    if err := c.BindJSON(&oneday); err != nil {
        return oneday, err
    }
    return oneday, nil
}
Run Code Online (Sandbox Code Playgroud)

bindOneDay返回错误EOF。因为也许c.Request.Body0

我想user_id从中间件的请求体中获取。如何做到没有问题c.Request.Body成为0

hob*_*bbs 6

您只能从客户端读取 Body 一次。数据是从用户那里流出的,他们不会再发送了。如果您想多次读取它,则必须将整个内容缓存在内存中,如下所示:

bodyCopy := new(bytes.Buffer)
// Read the whole body
_, err := io.Copy(bodyCopy, req.Body)
if err != nil {
    return err
}
bodyData := bodyCopy.Bytes()
// Replace the body with a reader that reads from the buffer
req.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
// Now you can do something with the contents of bodyData,
// like passing it to json.Unmarshal
Run Code Online (Sandbox Code Playgroud)

请注意,将整个请求缓冲到内存中意味着用户可能会导致您分配无限内存——您可能应该在前端代理中阻止它或使用io.LimitedReader来限制您将缓冲的数据量。

您还可以读取整个身体前解组可以开始工作-这大概是没什么大不了的,但你可以用做的更好io.TeeReader,并json.NewDecoder如果你是这样的倾向。

当然,更好的是找出一种重组代码的方法,这样就不需要缓冲主体并对其进行两次解码。