Rainbond/util/govalidator/validator.go
2020-09-06 11:11:11 +08:00

119 lines
3.5 KiB
Go

package validator
import (
"fmt"
"net/http"
"net/url"
"strings"
)
const (
tagIdentifier = "validate" //tagName idetify the struct tag for govalidator
tagSeparator = "|" //tagSeparator use to separate tags in struct
)
type (
// MapData represents basic data structure for govalidator Rules and Messages
MapData map[string][]string
// Options describes configuration option for validator
Options struct {
Data interface{} // Data represents structure for JSON body
Request *http.Request
RequiredDefault bool // RequiredDefault represents if all the fields are by default required or not
UniqueKey bool // UniqueKey set prefix (type name) in field name for ValidateJSON
Rules MapData // Rules represents rules for form-data/x-url-encoded/query params data
Messages MapData // Messages represents custom/localize message for rules
}
// Validator represents a validator with options
Validator struct {
Opts Options // Opts contains all the options for validator
}
)
// New return a new validator object using provided options
func New(opts Options) *Validator {
return &Validator{Opts: opts}
}
// getMessage return if a custom message exist against the field name and rule
// if not available it return an empty string
func (v *Validator) getCustomMessage(field, rule string) string {
if msgList, ok := v.Opts.Messages[field]; ok {
for _, m := range msgList {
//if rules has params, remove params. e.g: between:3,5 would be between
if strings.Contains(rule, ":") {
rule = strings.Split(rule, ":")[0]
}
if strings.HasPrefix(m, rule+":") {
return strings.TrimPrefix(m, rule+":")
}
}
}
return ""
}
// SetDefaultRequired change the required behavior of fields
// Default value if false
// If SetDefaultRequired set to true then it will mark all the field in the rules list as required
func (v *Validator) SetDefaultRequired(required bool) {
v.Opts.RequiredDefault = required
}
// Validate validate request data like form-data, x-www-form-urlencoded and query params
// see example in README.md file
func (v *Validator) Validate() url.Values {
// if request object and rules not passed rise a panic
if len(v.Opts.Rules) == 0 || v.Opts.Request == nil {
panic(errValidateArgsMismatch)
}
errsBag := url.Values{}
// clean rules
v.keepRequiredField()
for field, rules := range v.Opts.Rules {
reqVal := strings.TrimSpace(v.parseAndGetVal(field))
for _, rule := range rules {
if !isRuleExist(rule) {
panic(fmt.Errorf("validator: %s is not a valid rule", rule))
}
v := fieldValidator{
errsBag: errsBag,
field: field,
value: reqVal,
rule: rule,
message: v.getCustomMessage(field, rule),
}
v.run()
// validate if custom rules exist
validateCustomRules(field, reqVal, rule, errsBag)
}
}
return errsBag
}
// parseAndGetVal parse the incoming request object and return the value associated with the key
func (v *Validator) parseAndGetVal(key string) string {
// v.Opts.Request.ParseMultipartForm(1024)
// r.ParseForm()
return v.Opts.Request.Form.Get(key)
}
// keepRequiredField remove non required rules field from rules if requiredDefault field is false
func (v *Validator) keepRequiredField() {
v.Opts.Request.ParseMultipartForm(1024)
//r.ParseForm()
inputs := v.Opts.Request.Form
if !v.Opts.RequiredDefault {
for k, r := range v.Opts.Rules {
if _, ok := inputs[k]; !ok {
if !isContainRequiredField(r) {
delete(v.Opts.Rules, k)
}
}
}
}
}