Repository: gin-gonic/gin (language: go)

Issue:
unable to pass a pointer to struct level validator (works with a workaround)
## Description

I am trying to create a struct level validator that would modify the original struct. The reason for that is that I'm expecting the user to pass a hash and I need to convert this hash to a primary key (int). Without this ability, I would have to run the same query twice (first query inside the validator to validate the hash and then second query outside of the validator to get the actual PK). I have already found out the reason for my unexpected behavior and it is located in gin/binding/default_validator.go:

```go
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
    // (cut for brevity)
	value := reflect.ValueOf(obj)
	switch value.Kind() {
	case reflect.Ptr:
		return v.ValidateStruct(value.Elem().Interface())
```
so I believe what's happening is that my pointer gets de-referenced and even if I make changes inside the validator function they are lost.

I've thought of many ways to approach this but I can't figure out any other way (than passing a pointer). if I would use a field validator, it can only return a boolean, plus I don't have access to the struct so the StructLevel validation feels like a better way to accomplish this.

## How to reproduce

```go
package main

import (
	"github.com/gin-gonic/gin"
)

type MyStruct struct {
    PublicField string `binding:"required" json:"some_field"`
    ProtectedField uint
}

func validateAndModifyStruct(sl validator.StructLevel) {
    // the following code raises a 500 exception because sl.Top().Interface()
    // is of type MyStruct, not *MyStruct (is de-referenced)
    var ms *MyStruct = sl.Top().Interface().(*MyStruct)
    ms.ProtectedField = 123
}

func main() {
	g := gin.Default()

	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterStructValidation(validateAndModifyStruct, MyStruct{})
	}
	g.POST("/hello/", func(c *gin.Context) {
                var ms MyStruct
                c.ShouldBind(&ms)
		c.JSON(200, ms)
	})
	g.Run(":9000")
}
```

## Expectations

the expectation would be for the struct to be modified.

I *am* able to make this work on current gin code by converting c.ShouldBind into the following code (again, error handling cut out for brevity):

```go
        var ms MyStruct
	decoder := json.NewDecoder(c.Request.Body)
	decoder.Decode(&ms)
	binding.Validator.Engine().(*validator.Validate).Struct(&ms)
```

it works as expected but it definetely feels hacky. What I would like to understand would be - why is gin behaving this way? It definetely feels intended. Is this for security reason?

## Environment

- go version: 1.18
- gin version (or commit ref): 1.7.7
- operating system:



Discussion / hints:

Fix patch:
diff --git a/binding/default_validator.go b/binding/default_validator.go
index c03afe75be..63b3021b11 100644
--- a/binding/default_validator.go
+++ b/binding/default_validator.go
@@ -54,7 +54,10 @@ func (v *defaultValidator) ValidateStruct(obj any) error {
 	value := reflect.ValueOf(obj)
 	switch value.Kind() {
 	case reflect.Ptr:
-		return v.ValidateStruct(value.Elem().Interface())
+		if value.Elem().Kind() != reflect.Struct {
+			return v.ValidateStruct(value.Elem().Interface())
+		}
+		return v.validateStruct(obj)
 	case reflect.Struct:
 		return v.validateStruct(obj)
 	case reflect.Slice, reflect.Array:


Test patch:
diff --git a/binding/validate_test.go b/binding/validate_test.go
index 801bd9b7c5..1fc15ff079 100644
--- a/binding/validate_test.go
+++ b/binding/validate_test.go
@@ -192,6 +192,30 @@ func TestValidatePrimitives(t *testing.T) {
 	assert.Equal(t, "value", str)
 }
 
+type structModifyValidation struct {
+	Integer int
+}
+
+func toZero(sl validator.StructLevel) {
+	var s *structModifyValidation = sl.Top().Interface().(*structModifyValidation)
+	s.Integer = 0
+}
+
+func TestValidateAndModifyStruct(t *testing.T) {
+	// This validates that pointers to structs are passed to the validator
+	// giving us the ability to modify the struct being validated.
+	engine, ok := Validator.Engine().(*validator.Validate)
+	assert.True(t, ok)
+
+	engine.RegisterStructValidation(toZero, structModifyValidation{})
+
+	s := structModifyValidation{Integer: 1}
+	errs := validate(&s)
+
+	assert.Nil(t, errs)
+	assert.Equal(t, s, structModifyValidation{Integer: 0})
+}
+
 // structCustomValidation is a helper struct we use to check that
 // custom validation can be registered on it.
 // The `notone` binding directive is for custom validation and registered later.