Skip to content

Instantly share code, notes, and snippets.

@hungdh0x5e
Last active May 9, 2020 09:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hungdh0x5e/cd3c6153029a852cbf482b1c62623735 to your computer and use it in GitHub Desktop.
Save hungdh0x5e/cd3c6153029a852cbf482b1c62623735 to your computer and use it in GitHub Desktop.
Custom gin validator error message
// Before init Gin
err := validation.InitValidator()
// In controller or struct validate
err := ctx.ShouldBindJSON(&request)
if err != nil {
for _, fieldErr := range err.(validator.ValidationErrors) {
errMsg := errors.New(fieldErr.Translate(validation.CustomTranslator))
vlog.Error(vgin.GetAppContext(ctx), "Failed to bind request body", errMsg)
vgin.BuildErrorResponse(ctx, verrors.NewInvalidRequestFormatError(errMsg))
return // exit on first error
}
}
// =====================
package validation
import (
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
"gitlab.id.vin/loyalty/loyalty-checkout/common"
"log"
"reflect"
"regexp"
"strings"
)
var CustomTranslator ut.Translator
const (
IDByType = "id_by_type"
)
func InitValidator() error {
validate := binding.Validator.Engine().(*validator.Validate)
registerTagNameJSON(validate)
registerTranslator(validate)
_ = registerIDByTypeValidation(validate)
return nil
}
func registerTagNameJSON(validate *validator.Validate) {
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
}
func registerTranslator(validate *validator.Validate) {
translator := en.New()
uni := ut.New(translator, translator)
found := false
CustomTranslator, found = uni.GetTranslator("en")
if !found {
log.Fatal("translator not found")
}
if err := en_translations.RegisterDefaultTranslations(validate, CustomTranslator); err != nil {
log.Fatal(err)
}
}
func registerIDByTypeValidation(validate *validator.Validate) error {
err := validate.RegisterValidation(IDByType, validateIDByType)
if err != nil {
return err
}
_ = validate.RegisterTranslation(IDByType, CustomTranslator, func(ut ut.Translator) error {
return ut.Add(IDByType, "{0} is not valid with id_type", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T(IDByType, fe.Field())
return t
})
return nil
}
func validateIDByType(fl validator.FieldLevel) bool {
idTypeValue, idTypeKind, _, ok := fl.GetStructFieldOK2()
if !ok || idTypeKind.String() != "string" {
return false
}
if idTypeValue.String() != common.IDTypeMobile {
return true
}
phoneNumberRegex := regexp.MustCompile("^0\\d{9}$")
return phoneNumberRegex.MatchString(fl.Field().String())
}
@hungdh0x5e
Copy link
Author

Example

Request

POST /loyalty/checkout/v1/membership HTTP/1.1
Authorization: Bearer 29z
Content-Type: application/json

{
    "id_type": "OTHER",
    "id": "0988310608"
}

Response

{
    "error": {
        "code": "LYT_000_003",
        "message": "Bad Request",
        "inner": {
            "message": "id_type must be one of [MOBILE USER_ID CSN]"
           // raw message:  "Key: 'MembershipRequest.CustomerRequest.IDType' Error:Field validation for 'IDType' failed on the 'oneof' tag"
        }
    },
    "meta": {
        "request_id": "3ffd4f61-1618-4fb9-bde1-06c3ddb96f6b",
        "code": 400000003,
        "message": "Bad Request"
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment