Data Validation
Data validators, like go-playground/validator utilize struct tags, which are intuitive and concise. I'd like to implement a simple validator for fun. For production use, it's best to choose a library.
Write a Validator
If you haven't initialized Go modules, do so with:
$ go mod init wdgfs
Then, add validation/main.go:
package validation
import (
"fmt"
"reflect"
"regexp"
"strings"
)
var validators = map[string]func(param string, value interface{}) bool{
"required": func(param string, value interface{}) bool {
return value != nil
},
"email": func(param string, value interface{}) bool {
if val, ok := value.(string); ok {
pattern := regexp.MustCompile(`^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$`)
return pattern.MatchString(val)
}
return false
},
}
func Validate(data interface{}) error {
val := reflect.ValueOf(data)
for i := range val.NumField() {
field := val.Type().Field(i)
for _, rule := range strings.Split(field.Tag.Get("validate"), ",") {
if validator, ok := validators[rule]; ok {
if !validator(rule, val.Field(i).Interface()) {
return fmt.Errorf("%s failed validation rule %s", field.Name, rule)
}
} else {
return fmt.Errorf("invalide validation rule %s", rule)
}
}
}
return nil
}
Use the Validator
Update server.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"wdgfs/validation"
)
type User struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
}
func main() {
http.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
var p User
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
if err := validation.Validate(p); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "User: %+v", p)
})
if err := http.ListenAndServe(":3000", nil); err != nil {
panic(err)
}
}
Test the Validator
$ curl -i http://localhost:3000/users -d '{"name": "Alice", "email": "alice@test"}'
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Tue, 28 Oct 2024 01:13:21 GMT
Content-Length: 35
Email failed validation rule email
$ curl -i http://localhost:3000/users -d '{"name": "Alice", "email": "alice@test.co"}'
HTTP/1.1 200 OK
Date: Tue, 28 Oct 2024 01:13:25 GMT
Content-Length: 38
Content-Type: text/plain; charset=utf-8
User: {Name:Alice Email:alice@test.co}