Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] EOF when using ShouldBindJSON #4035

Open
victor-gapeev opened this issue Aug 20, 2024 · 6 comments
Open

[Bug] EOF when using ShouldBindJSON #4035

victor-gapeev opened this issue Aug 20, 2024 · 6 comments

Comments

@victor-gapeev
Copy link

victor-gapeev commented Aug 20, 2024

Description

When I use ctx.ShouldBindJSON(&body), it quietly close request without any response. Other requests with same method works normally.

How to reproduce

Model:

type FastEnterDto struct {
	UUID     string `json:"uuid" binding:"required, max=256"`
	DeviceId int    `json:"deviceId" binding:"required"`
}

Handler:

group.POST("fastenter", func(ctx *gin.Context) {
        var body entities.FastEnterDto
        
        fmt.Println("start bind")
        err := ctx.ShouldBindJSON(&body)
        fmt.Println("end bind, err", err.Error())
        if err != nil {
	        ctx.AbortWithError(http.StatusBadRequest, ErrBadRequest)
	        return
        }
        ...
}

Expectations

Correct binding.

Actual result

When I`m making a request, I get the next log:

start bind

BUT, if I add lines to print the response as a string before binding, like this:

group.POST("fastenter", func(ctx *gin.Context) {
        s, _ := ioutil.ReadAll(ctx.Request.Body)
        fmt.Println(string(s))
        var body entities.FastEnterDto
        ...

I got:

{"uuid":"qCXDPm2qbtuyHNUrlSi80","deviceId":725087484513740}
start bind
end bind, err EOF

I'm sure I don't bind body twice anywhere. I use only cors and log middlewares. This behavior doesn't make any sense and strongly needs explanation.

My code which send requests:

const response = await fetch(baseUrl + path, {
        method: "POST",
        body: JSON.stringify(body),
        credentials: "include",
        headers: {
	        "Content-Type": "application/json",
        },
})

Environment

  • go version: go version go1.22.6 windows/amd64
  • gin version (or commit ref): v1.10.0
  • operating system: windows 11
@k8scommander
Copy link

Trying to reproduce it, for this part:

        err := ctx.ShouldBindJSON(&body)
        fmt.Println("end bind, err", err.Error())

if err == nil, printing err.Error() will produce an exception

For the second part:
s, _ := ioutil.ReadAll(ctx.Request.Body)
reading the request body will prevent you from binding at as it's already been read.

@JimChenWYU
Copy link

image

There is an extra space.

@rcarrion2
Copy link

I have the same problem. I had to downgrade to 1.9.1.

@blkcor
Copy link

blkcor commented Aug 26, 2024

Trying to reproduce it, for this part:

        err := ctx.ShouldBindJSON(&body)
        fmt.Println("end bind, err", err.Error())

if err == nil, printing err.Error() will produce an exception

For the second part: s, _ := ioutil.ReadAll(ctx.Request.Body) reading the request body will prevent you from binding at as it's already been read.

Yes, that is.I think there is no bug actually

@RedCrazyGhost
Copy link
Contributor

@victor-gapeev There may be some logical problems with the code you write, and request.go explains Body io.ReadCloser and has a rudimentary understanding of its use

https://github.com/golang/go/blob/fc9f02c7aec81bcfcc95434d2529e0bb0bc03d66/src/net/http/request.go#L174-L196

@rcarrion2 @victor-gapeev If you need to use the data in the Body repeatedly, you can use methods that support caching the Body

Methods:

  • ctx.ShouldBindBodyWithJSON(&body)
  • ctx.ShouldBindBodyWithXML(&body)
  • ctx.ShouldBindBodyWithYAML(&body)
  • ctx.ShouldBindBodyWithTOML(&body)

gin/context.go

Lines 777 to 817 in 3cb3067

func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) {
var body []byte
if cb, ok := c.Get(BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
body = cbb
}
}
if body == nil {
body, err = io.ReadAll(c.Request.Body)
if err != nil {
return err
}
c.Set(BodyBytesKey, body)
}
return bb.BindBody(body, obj)
}
// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON).
func (c *Context) ShouldBindBodyWithJSON(obj any) error {
return c.ShouldBindBodyWith(obj, binding.JSON)
}
// ShouldBindBodyWithXML is a shortcut for c.ShouldBindBodyWith(obj, binding.XML).
func (c *Context) ShouldBindBodyWithXML(obj any) error {
return c.ShouldBindBodyWith(obj, binding.XML)
}
// ShouldBindBodyWithYAML is a shortcut for c.ShouldBindBodyWith(obj, binding.YAML).
func (c *Context) ShouldBindBodyWithYAML(obj any) error {
return c.ShouldBindBodyWith(obj, binding.YAML)
}
// ShouldBindBodyWithTOML is a shortcut for c.ShouldBindBodyWith(obj, binding.TOML).
func (c *Context) ShouldBindBodyWithTOML(obj any) error {
return c.ShouldBindBodyWith(obj, binding.TOML)
}
// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON).
func (c *Context) ShouldBindBodyWithPlain(obj any) error {
return c.ShouldBindBodyWith(obj, binding.Plain)
}

@HHC26
Copy link

HHC26 commented Sep 24, 2024

IF gin automatically implements binding routing, parameter verification, and generating swaggers, which can reduce a lot of workload when developing the web

type UserSearchReq struct {
g.Meta path:"/user/list" tags:"sysUser" method:"get" summary:"xxx"
DeptId string p:"deptId"
RoleId uint p:"roleId"
Status string p:"status"
}
type UserSearchRes struct {
g.Meta mime:"application/json"
UserList []*model.SysUserRoleDeptRes json:"userList"
}

type UserAddReq struct {
g.Meta path:"/user/add" tags:"sysUser" method:"post" summary:"xxx"
UserName string p:"userName" v:"required#User account cannot be empty"
Password string p:"password" v:"required|password#PWD account cannot be empty"
UserSalt string
}
type UserAddRes struct {
}

func GetUserList(c *gin.Context, req *UserSearchReq) (res *UserSearchRes, err error) {
// todo
}

func UserAddReq(c *gin.Context, req *UserAddReq) (res *UserAddRes, err error) {
// todo
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants