This project example is an example of a validator struct in golang, using package go-playground/validator/v10.
Project Initialization
Let’s start by creating a new directory for our project:
mkdir go-validator && cd go-validator
initialize our Go module:
go mod init github.com/adamnasrudin03/go-validator
Now from the root of the project, let’s create 3 subdirectories:
mkdir dto controllers helpers
install package validator:
go get github.com/go-playground/validator/v10
Create DTO Structure
create new file in directory dto
, name it user.go
and add the following code:
package dto
type UserRequest struct {
Name string `json:"name" validate:"required,min=4"`
Email string `json:"email" validate:"required,email"`
Age uint64 `json:"age" validate:"gte=18,lte=120"`
Address string `json:"address" validate:"required,min=10"`
Phone string `json:"phone" validate:"required,e164"` // https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-E_164_Phone_Number_String
}
Helpers response Http
In directory helpers
, create new directory response
, then create new file name it response.go
and add the following code:
package response
import (
"encoding/json"
"errors"
"net/http"
)
type Response struct {
Message interface{} `json:"message,omitempty"`
Errors interface{} `json:"errors,omitempty"`
Data interface{} `json:"data,omitempty"`
}
func NewResponse(input interface{}) (resp Response) {
switch input := input.(type) {
case []string:
resp = Response{
Errors: input,
}
case error:
resp = Response{
Errors: input.Error(),
}
case string:
resp = Response{
Message: input,
}
default:
resp = Response{
Data: input,
}
}
return resp
}
func APIResponse(w http.ResponseWriter, r *http.Request, data interface{}, code int) {
jsonBytes, err := json.Marshal(NewResponse(data))
if err != nil {
APIResponseInternalServerError(w, r)
return
}
w.WriteHeader(code)
w.Write(jsonBytes)
}
func APIResponseInternalServerError(w http.ResponseWriter, r *http.Request) {
APIResponse(w, r, errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError)
}
func APIResponseNotFound(w http.ResponseWriter, r *http.Request) {
APIResponse(w, r, errors.New(http.StatusText(http.StatusNotFound)), http.StatusNotFound)
}
func PanicRecover(w http.ResponseWriter, r *http.Request) {
if rc := recover(); rc != nil {
APIResponseInternalServerError(w, r)
}
}
Helpers function handler custom error validator
create new file in directory helpers, create directory name it validator
then create new file name it errors.go
and add the following code:
package validator
import (
"fmt"
"github.com/go-playground/validator/v10"
)
func FormatValidationError(err error) []string {
if fieldErrors, ok := err.(validator.ValidationErrors); ok {
resp := make([]string, len(fieldErrors))
for i, e := range fieldErrors {
switch e.Tag() {
case "required":
resp[i] = fmt.Sprintf("%s is a required field", e.Field())
case "email":
resp[i] = fmt.Sprintf("%v must be type %v", e.Field(), e.Tag())
case "e164":
resp[i] = fmt.Sprintf("%v must be type phone number", e.Field())
case "gte":
resp[i] = fmt.Sprintf("%v must be greater than or equal to %v", e.Field(), e.Param())
case "lte":
resp[i] = fmt.Sprintf("%v must be less than or equal to %v", e.Field(), e.Param())
default:
resp[i] = fmt.Sprintf("%v is %v %v", e.Field(), e.Tag(), e.Param())
}
}
return resp
}
return []string{}
}
Create Handler Http Request
create new file in directory controllers
, name it user.go
and add the following code:
package controllers
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/adamnasrudin03/go-validator/dto"
"github.com/adamnasrudin03/go-validator/helpers/response"
help_validator "github.com/adamnasrudin03/go-validator/helpers/validator"
"github.com/go-playground/validator/v10"
)
type UserController interface {
HandlerCustomValidation(w http.ResponseWriter, r *http.Request)
}
type userHandler struct {
Validate *validator.Validate
}
func NewUserController(validate *validator.Validate) UserController {
return &userHandler{
Validate: validate,
}
}
func (h *userHandler) HandlerCustomValidation(w http.ResponseWriter, r *http.Request) {
defer response.PanicRecover(w, r)
reqBody, err := io.ReadAll(r.Body)
if err != nil {
response.APIResponse(w, r, errors.New("invalid request"), http.StatusBadRequest)
return
}
input := dto.UserRequest{}
err = json.Unmarshal(reqBody, &input)
if err != nil {
response.APIResponse(w, r, errors.New("invalid request"), http.StatusBadRequest)
return
}
// validate input
err = h.Validate.Struct(input)
if err != nil {
response.APIResponse(w, r, help_validator.FormatValidationError(err), http.StatusBadRequest)
return
}
response.APIResponse(w, r, input, http.StatusOK)
}
Create Main Function
in root directory, create new file name it main.go
and add the following code:
package main
import (
"log"
"net/http"
"github.com/adamnasrudin03/go-validator/controllers"
"github.com/adamnasrudin03/go-validator/helpers/response"
"github.com/go-playground/validator/v10"
)
func main() {
port := "1200"
mux := http.NewServeMux()
validator := validator.New()
userController := controllers.NewUserController(validator)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
defer response.PanicRecover(w, r)
switch {
case r.URL.Path == "/" && r.Method == "GET":
response.APIResponse(w, r, "Welcome my application", http.StatusOK)
case r.URL.Path == "/custom-validation" && r.Method == "POST":
userController.HandlerCustomValidation(w, r)
default:
response.APIResponseNotFound(w, r)
}
})
log.Printf("Server is running on port %v\n", port)
err := http.ListenAndServe(":"+port, mux)
if err != nil {
log.Fatal(err)
}
}
Usage and testing example
open terminal and run following command:
go run main.go
test endpoint using postman or other tools, example response:
- All field errors
- Two field errors
- Success
Conclusion
This is an example of Go validator struct validation within a Golang project. For other validation options or documentation, refer to the go-playground/validator/v10 package. If you'd like to see the full code, you can visit github.com/adamnasrudin03/go-validator. Enjoy and happy coding!