In the previous blog, we had gone over struct validations in golang using validator library. Continuing on, in this blog, we'll cover custom validations.
Let's say for the below struct:
type Config struct {
ServerUrl string
AppPort int
}
We could validate our struct with validations provided by the library like:
import (
"fmt"
"github.com/go-playground/validator"
"strings"
)
func Validate(config Config) bool {
validate := validator.New()
validationErr := validate.Struct(config)
if validationErr != nil {
return false
}
return true
}
Now onto the topic of custom validations.
Say, we want to check whether ServerUrl field is an HTTPS URL
The straight forward way would be to introduce a function:
func customValidator(config Config) bool {
value := config.ServerUrl
if !strings.HasPrefix(value, "https://") {
return false
}
return true
}
and remember to invoke it in our Validate() func ๐ก
Or we could do it the idiomatic way and leverage validator library โค๏ธ
The custom validation function would then look like:
import (
"github.com/go-playground/validator"
"strings"
)
func CustomValidation(fl validator.FieldLevel) bool {
value := fl.Field().String()
if !strings.HasPrefix(value, "https://") {
return false
}
return true
}
"Why this weird function signature?" ๐ค you ask - hold on to that question for a short while...
The next step is to register our shiny ๐ new rule with the validator library.
import (
"fmt"
"github.com/go-playground/validator"
"strings"
)
func Validate(config Config) bool {
validate := validator.New()
// Registering the new rule "is_https" ...
err := validate.RegisterValidation("is_https", CustomValidation)
if err != nil {
fmt.Println("Error registering custom validation :", err.Error())
}
validationErr := validate.Struct(config)
if validationErr != nil {
return false
}
return true
}
Now that our rule is registered let us attach the tag to our struct's field:
type Config struct {
// Careful, the tag name should be identical to the one registered
ServerUrl string `validate:"is_https"`
AppPort int
}
And Voilร ! ๐ we are done!
Now, back to the question of this weird signature of CustomValidation() function -
Observe validate.RegisterValidation() takes in:
- A
tagof how you want to refer the validation in your struct. - A
validator.Func- essentially a function of type:func(fl FieldLevel) bool(reference source code)
Even validations at struct level - ones that involve multiple fields of the same struct - are easily achievable with similar function signature: func(fl StructLevel) bool
This is how the final code looks like: https://github.com/PankhudiB/go-config-validation/blob/main/configuration/custom_validation.go
Happy Coding! ๐ฉโ๐ป
Top comments (1)
wonderful. I was tying to write a FHIR server in go which also should validate a bunch of resources sent over to the server and inform of the client what might be wrong with the structure of a resource. the "is_http" part was nice :-)