package ginctx import ( "clapclap/internal/apperr" "encoding/json" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" ) func GetDto[T any](c *gin.Context) (T, error) { return Get[T](c, "dto") } // handleBindError intercepts Gin crashes and turns them into beautiful AppErrors. func handleBindError(err error) error { var valErrs validator.ValidationErrors var syntaxErr *json.SyntaxError var unmarshalTypeErr *json.UnmarshalTypeError // 1. Check for missing/invalid fields if errors.As(err, &valErrs) { var details []string for _, fieldErr := range valErrs { msg := fmt.Sprintf("Field '%s' failed validation on the '%s' tag", fieldErr.Field(), fieldErr.Tag()) details = append(details, msg) } args := append([]string{"Validation failed"}, details...) return apperr.NewValidationError(err, args...) } // 2. Check for broken JSON text if errors.As(err, &syntaxErr) || errors.As(err, &unmarshalTypeErr) { return apperr.NewBadRequestError(err, "Malformed JSON payload", err.Error()) } // 3. Fallback return apperr.NewBadRequestError(err, "Invalid request data") } // Binders func ShouldBindJSON[T any](c *gin.Context) { var dto T if err := c.ShouldBindJSON(&dto); err != nil { appErr := handleBindError(err) apperr.AbortWithError(c, appErr) return } c.Set("dto", dto) c.Next() } func ShouldBindQuery[T any](c *gin.Context) { var dto T if err := c.ShouldBindQuery(&dto); err != nil { appErr := handleBindError(err) apperr.AbortWithError(c, appErr) return } c.Set("dto", dto) c.Next() }