gin icon indicating copy to clipboard operation
gin copied to clipboard

ShouldBindBodyWithJSON : binding:"required" + bool field causes validation error when passing false

Open chencheng8888 opened this issue 8 months ago โ€ข 4 comments

โ— Bug: binding:"required" fails on bool when value is false ๐Ÿ” Description When using binding:"required" on a bool field in a Gin handler, passing false causes validation to fail, even though the field is clearly present in the JSON body.

This is unexpected and breaks legitimate use cases where false is a valid, required value.

โœ… Minimal reproducible example

func ParseRequestBody(c *gin.Context, req any) error {
	err := c.ShouldBindBodyWithJSON(req)
	if err != nil {
		return err
	}
	return nil
}

type SimpleRequest struct {
	Donate bool `json:"donate" binding:"required"`
}

func TestParseRequestBody(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.POST("/test", func(c *gin.Context) {
		var req SimpleRequest
		if err := ParseRequestBody(c, &req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, req)
	})

	tests := []struct {
		name         string
		requestBody  string
		expectedCode int
	}{
		{
			name:         "donate=false",
			requestBody:  `{"donate": false}`,
			expectedCode: http.StatusOK,
		},
		{
			name:         "No donate field is passed",
			requestBody:  `{}`,
			expectedCode: http.StatusBadRequest,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			w := httptest.NewRecorder()
			req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer([]byte(tt.requestBody)))
			req.Header.Set("Content-Type", "application/json")

			router.ServeHTTP(w, req)
			assert.Equal(t, tt.expectedCode, w.Code)
		})
	}
}

OutPut: (I put some logs) === RUN TestParseRequestBody --- FAIL: TestParseRequestBody (0.02s) === RUN TestParseRequestBody/donate=false 2025-04-16T01:08:27.949+0800 ERROR req/req.go:13 Error parsing request: Key: 'SimpleRequest.Donate' Error:Field validation for 'Donate' failed on the 'required' tag book-management/internal/pkg/req.ParseRequestBody C:/Users/29638/Desktop/book-management/internal/pkg/req/req.go:13 book-management/internal/pkg/req.TestParseRequestBody.func1 C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:22 github.com/gin-gonic/gin.(*Context).Next D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/context.go:185 github.com/gin-gonic/gin.(*Engine).handleHTTPRequest D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:633 github.com/gin-gonic/gin.(*Engine).ServeHTTP D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:589 book-management/internal/pkg/req.TestParseRequestBody.func2 C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:53 testing.tRunner D:/Scoop/apps/go/1.24.2/src/testing/testing.go:1792 req_test.go:56: Error Trace: C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:56 Error: Not equal: expected: 200 actual : 400 Test: TestParseRequestBody/donate=false Messages: status code should be 200 --- FAIL: TestParseRequestBody/donate=false (0.02s)

=== RUN TestParseRequestBody/No_donate_field_is_passed --- PASS: TestParseRequestBody/No_donate_field_is_passed (0.00s)

FAIL

๐Ÿงช Actual Behavior Sending { "donate": false } returns 400 Bad Request

Even though the field is present and valid

โœ… Expected Behavior Validation should pass when the JSON body includes "donate": false, because: The field is present

The value false is valid for a required bool๐Ÿค”

chencheng8888 avatar Apr 15 '25 17:04 chencheng8888

type SimpleRequest struct { Donate *bool json:"donate" binding:"required" }

jiaxin149 avatar Apr 18 '25 17:04 jiaxin149

@jiaxin149 Rather than how to parse correctly, I am actually more curious about why this error occurs: "Error parsing request: Key: 'SimpleRequest.Donate' Error:Field validation for 'Donate' failed on the 'required' tag"๐Ÿค”

chencheng8888 avatar Apr 19 '25 07:04 chencheng8888

@jiaxin149 When passing true, there was no error โ€” could it be that false is being treated as a zero value?

chencheng8888 avatar Apr 19 '25 07:04 chencheng8888

The gin binding tag actually uses validator as its underlying implementation, and this issue provides a comprehensive answer to it.

RedCrazyGhost avatar Oct 16 '25 08:10 RedCrazyGhost