go 63 lines · 9 steps

Turning Gin binding errors into field messages

A Gin handler validates JSON with struct tags and returns clean, per-field error messages.

Explained by highlit
1package handlers
2 
3import (
4 "errors"
5 "net/http"
6 
7 "github.com/gin-gonic/gin"
8 "github.com/go-playground/validator/v10"
9)
10 
11type CreateUserRequest struct {
12 Name string `json:"name" binding:"required,min=2,max=50"`
13 Email string `json:"email" binding:"required,email"`
14 Password string `json:"password" binding:"required,min=8"`
15 Age int `json:"age" binding:"required,gte=18,lte=120"`
16 Website string `json:"website" binding:"omitempty,url"`
17}
18 
19func validationMessage(fe validator.FieldError) string {
20 switch fe.Tag() {
21 case "required":
22 return fe.Field() + " is required"
23 case "email":
24 return fe.Field() + " must be a valid email address"
25 case "min":
26 return fe.Field() + " must be at least " + fe.Param() + " characters"
27 case "max":
28 return fe.Field() + " must be at most " + fe.Param() + " characters"
29 case "gte":
30 return fe.Field() + " must be greater than or equal to " + fe.Param()
31 case "lte":
32 return fe.Field() + " must be less than or equal to " + fe.Param()
33 case "url":
34 return fe.Field() + " must be a valid URL"
35 default:
36 return fe.Field() + " is invalid"
37 }
38}
39 
40func CreateUser(c *gin.Context) {
41 var req CreateUserRequest
42 if err := c.ShouldBindJSON(&req); err != nil {
43 var ve validator.ValidationErrors
44 if errors.As(err, &ve) {
45 fields := make(map[string]string, len(ve))
46 for _, fe := range ve {
47 fields[fe.Field()] = validationMessage(fe)
48 }
49 c.JSON(http.StatusUnprocessableEntity, gin.H{"errors": fields})
50 return
51 }
52 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
53 return
54 }
55 
56 user, err := userService.Create(c.Request.Context(), req.Name, req.Email, req.Password, req.Age)
57 if err != nil {
58 c.JSON(http.StatusInternalServerError, gin.H{"error": "could not create user"})
59 return
60 }
61 
62 c.JSON(http.StatusCreated, user)
63}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Struct tags let you declare validation rules once, right next to the data they govern.
  2. 2Translating raw validator errors into a field-to-message map gives clients actionable, readable feedback.
  3. 3Distinguishing malformed JSON from failed validation lets you return the correct HTTP status for each failure mode.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Turning Gin binding errors into field messages — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code