在Golang中读取请求体两次

7
type ValidationModel struct {
    Name     string `json:"name" valid:"alpha,required~Name is required"`
    Email    string `json:"email" valid:"email~Enter a valid email.,required~Email is required."`
    Password string `json:"password" valid:"required~Password is required"`
}

validationModel := ValidationModel{}

json.NewDecoder(r.Body).Decode(&validationModel)

_, err := govalidator.ValidateStruct(validationModel)

首先,我使用govalidator验证请求正文。

type UserModel struct {
    ID           bson.ObjectId `json:"_id" bson:"_id"`
    Name         string        `json:"name" bson:"name"`
    Email        string        `json:"email" bson:"email"`
    Password     string        `json:"password,omitempty" bson:"-"`
    PasswordHash string        `json:"-" bson:"passwordHash"`
    Salt         string        `json:"-" bson:"salt"`
    Token        string        `json:"token,omitempty" bson:"-"`
}


user := models.UserModel{}
json.NewDecoder(r.Body).Decode(&user)

fmt.Println(user)

在验证请求后,我再次将请求体解码为用户结构体,但是请求体已经使用validationModel读取了一次,因此当我尝试将其重新解码为用户时,它没有给我任何值。
我可以想到两种解决方案:
  1. 将请求体存储在一个单独的变量中,并使用该变量两次。
  2. 将validationModel值复制到用户中。
但我不知道如何实现这些方法,哪种方法最好遵循。或者是否有其他可实现的解决方案?
提前感谢。

1
在你的情况下,你应该重构代码,避免两次读取主体。这样会更高效(尤其是当你有大量主体时),在解码之后验证解码数据要比解码两次更有效率。 - Jonathan Hall
@Flimzy,你能否解释一下如何重构代码?我还有另一种方法,可以将一个结构体的数据复制到另一个结构体中。我该怎么做? - sohamdodia
一般而言:不要有单独的“验证模型”——相反,应该验证“用户模型”。 - Jonathan Hall
1个回答

17

使用ioutil.ReadAll()可以轻松存储数据:

data, err := ioutil.ReadAll(r.Body)

如果你需要将dataio.Reader(与r.Body相同)的形式返回,则可以使用bytes.NewReader()

reader := bytes.NewReader(data)

实际上,r.Body 是一个 io.ReadCloser,因此如果需要,您可以使用ioutil.NopCloser()bytes.NewReader()一起使用:

reader := ioutil.NopCloser(bytes.NewReader(data))

4
当r.Body非常大(例如1GB字节)时,使用ioutil.ReadAll不是一个好主意。 - iwind
1
这是正确的,但是你可以通过使用 https://golang.org/pkg/io/#LimitedReader 来保护自己。 - poy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接