add url validation and test cases
add uri validation and test cases
pull/16/head
Dean Karn 10 years ago
parent 2073d33609
commit 4e4a2a2b8e
  1. 122
      baked_in.go
  2. 8
      doc.go
  3. 2
      regexes.go
  4. 2
      validator.go
  5. 106
      validator_test.go

@ -2,6 +2,7 @@ package validator
import ( import (
"fmt" "fmt"
"net/url"
"reflect" "reflect"
"strconv" "strconv"
) )
@ -9,28 +10,69 @@ import (
// BakedInValidators is the map of ValidationFunc used internally // BakedInValidators is the map of ValidationFunc used internally
// but can be used with any new Validator if desired // but can be used with any new Validator if desired
var BakedInValidators = map[string]ValidationFunc{ var BakedInValidators = map[string]ValidationFunc{
"required": required, "required": hasValue,
"len": length, "len": hasLengthOf,
"min": min, "min": hasMinOf,
"max": max, "max": hasMaxOf,
"lt": lt, "lt": isLt,
"lte": lte, "lte": isLte,
"gt": gt, "gt": isGt,
"gte": gte, "gte": isGte,
"alpha": alpha, "alpha": isAlpha,
"alphanum": alphanum, "alphanum": isAlphanum,
"numeric": numeric, "numeric": isNumeric,
"number": number, "number": isNumber,
"hexadecimal": hexadecimal, "hexadecimal": isHexadecimal,
"hexcolor": hexcolor, "hexcolor": isHexcolor,
"rgb": rgb, "rgb": isRgb,
"rgba": rgba, "rgba": isRgba,
"hsl": hsl, "hsl": isHsl,
"hsla": hsla, "hsla": isHsla,
"email": email, "email": isEmail,
"url": isURL,
"uri": isURI,
} }
func email(field interface{}, param string) bool { func isURI(field interface{}, param string) bool {
st := reflect.ValueOf(field)
switch st.Kind() {
case reflect.String:
_, err := url.ParseRequestURI(field.(string))
return err == nil
default:
panic(fmt.Sprintf("Bad field type %T", field))
}
}
func isURL(field interface{}, param string) bool {
st := reflect.ValueOf(field)
switch st.Kind() {
case reflect.String:
url, err := url.ParseRequestURI(field.(string))
if err != nil {
return false
}
if len(url.Scheme) == 0 {
return false
}
return err == nil
default:
panic(fmt.Sprintf("Bad field type %T", field))
}
}
func isEmail(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -43,7 +85,7 @@ func email(field interface{}, param string) bool {
} }
} }
func hsla(field interface{}, param string) bool { func isHsla(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -56,7 +98,7 @@ func hsla(field interface{}, param string) bool {
} }
} }
func hsl(field interface{}, param string) bool { func isHsl(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -69,7 +111,7 @@ func hsl(field interface{}, param string) bool {
} }
} }
func rgba(field interface{}, param string) bool { func isRgba(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -82,7 +124,7 @@ func rgba(field interface{}, param string) bool {
} }
} }
func rgb(field interface{}, param string) bool { func isRgb(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -95,7 +137,7 @@ func rgb(field interface{}, param string) bool {
} }
} }
func hexcolor(field interface{}, param string) bool { func isHexcolor(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -108,7 +150,7 @@ func hexcolor(field interface{}, param string) bool {
} }
} }
func hexadecimal(field interface{}, param string) bool { func isHexadecimal(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -121,7 +163,7 @@ func hexadecimal(field interface{}, param string) bool {
} }
} }
func number(field interface{}, param string) bool { func isNumber(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -134,7 +176,7 @@ func number(field interface{}, param string) bool {
} }
} }
func numeric(field interface{}, param string) bool { func isNumeric(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -147,7 +189,7 @@ func numeric(field interface{}, param string) bool {
} }
} }
func alphanum(field interface{}, param string) bool { func isAlphanum(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -160,7 +202,7 @@ func alphanum(field interface{}, param string) bool {
} }
} }
func alpha(field interface{}, param string) bool { func isAlpha(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -173,7 +215,7 @@ func alpha(field interface{}, param string) bool {
} }
} }
func required(field interface{}, param string) bool { func hasValue(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -187,7 +229,7 @@ func required(field interface{}, param string) bool {
} }
} }
func gte(field interface{}, param string) bool { func isGte(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -223,7 +265,7 @@ func gte(field interface{}, param string) bool {
} }
} }
func gt(field interface{}, param string) bool { func isGt(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -262,7 +304,7 @@ func gt(field interface{}, param string) bool {
// length tests whether a variable's length is equal to a given // length tests whether a variable's length is equal to a given
// value. For strings it tests the number of characters whereas // value. For strings it tests the number of characters whereas
// for maps and slices it tests the number of items. // for maps and slices it tests the number of items.
func length(field interface{}, param string) bool { func hasLengthOf(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -302,12 +344,12 @@ func length(field interface{}, param string) bool {
// number. For number types, it's a simple lesser-than test; for // number. For number types, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func min(field interface{}, param string) bool { func hasMinOf(field interface{}, param string) bool {
return gte(field, param) return isGte(field, param)
} }
func lte(field interface{}, param string) bool { func isLte(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -343,7 +385,7 @@ func lte(field interface{}, param string) bool {
} }
} }
func lt(field interface{}, param string) bool { func isLt(field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -383,9 +425,9 @@ func lt(field interface{}, param string) bool {
// value. For numbers, it's a simple lesser-than test; for // value. For numbers, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func max(field interface{}, param string) bool { func hasMaxOf(field interface{}, param string) bool {
return lte(field, param) return isLte(field, param)
} }
// asInt retuns the parameter as a int64 // asInt retuns the parameter as a int64

@ -227,6 +227,14 @@ Here is a list of the current built in validators:
This may not conform to all possibilities of any rfc standard, but neither This may not conform to all possibilities of any rfc standard, but neither
does any email provider accept all posibilities... does any email provider accept all posibilities...
(Usage: email) (Usage: email)
url
This validates that a strings value contains a valid url
This will accept any url the golang request uri accepts but must contain
a schema for example http:// or rtmp://
(Usage: url)
uri
This validates that a strings value contains a valid uri
This will accept any uri the golang request uri accepts (Usage: uri)
Validator notes: Validator notes:

@ -14,6 +14,8 @@ const (
hslRegexString = "^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$" hslRegexString = "^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$"
hslaRegexString = "^hsla\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0.[1-9]*)|[01])\\s*\\)$" hslaRegexString = "^hsla\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" emailRegexString = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
// urlRegexString = `^((ftp|http|https):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|((www\.)?)?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?_?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?)|localhost)(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`
// urlRegexString = "^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\\.(?:[a-z\u00a1-\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$"
) )
var ( var (

@ -267,7 +267,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return nil return nil
} }
if strings.Contains(tag, omitempty) && !required(f, "") { if strings.Contains(tag, omitempty) && !hasValue(f, "") {
return nil return nil
} }

@ -121,6 +121,112 @@ func AssertMapFieldError(s map[string]*validator.FieldValidationError, field str
c.Assert(val.ErrorTag, Equals, expectedTag) c.Assert(val.ErrorTag, Equals, expectedTag)
} }
func (ms *MySuite) TestUrl(c *C) {
var tests = []struct {
param string
expected bool
}{
{"http://foo.bar#com", true},
{"http://foobar.com", true},
{"https://foobar.com", true},
{"foobar.com", false},
{"http://foobar.coffee/", true},
{"http://foobar.中文网/", true},
{"http://foobar.org/", true},
{"http://foobar.org:8080/", true},
{"ftp://foobar.ru/", true},
{"http://user:pass@www.foobar.com/", true},
{"http://127.0.0.1/", true},
{"http://duckduckgo.com/?q=%2F", true},
{"http://localhost:3000/", true},
{"http://foobar.com/?foo=bar#baz=qux", true},
{"http://foobar.com?foo=bar", true},
{"http://www.xn--froschgrn-x9a.net/", true},
{"", false},
{"xyz://foobar.com", true},
{"invalid.", false},
{".com", false},
{"rtmp://foobar.com", true},
{"http://www.foo_bar.com/", true},
{"http://localhost:3000/", true},
{"http://foobar.com#baz=qux", true},
{"http://foobar.com/t$-_.+!*\\'(),", true},
{"http://www.foobar.com/~foobar", true},
{"http://www.-foobar.com/", true},
{"http://www.foo---bar.com/", true},
{"mailto:someone@example.com", true},
{"irc://irc.server.org/channel", true},
{"irc://#channel@network", true},
{"/abs/test/dir", false},
{"./rel/test/dir", false},
}
for _, test := range tests {
err := validator.ValidateFieldByTag(test.param, "url")
if test.expected == true {
c.Assert(err, IsNil)
} else {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "url")
}
}
}
func (ms *MySuite) TestUri(c *C) {
var tests = []struct {
param string
expected bool
}{
{"http://foo.bar#com", true},
{"http://foobar.com", true},
{"https://foobar.com", true},
{"foobar.com", false},
{"http://foobar.coffee/", true},
{"http://foobar.中文网/", true},
{"http://foobar.org/", true},
{"http://foobar.org:8080/", true},
{"ftp://foobar.ru/", true},
{"http://user:pass@www.foobar.com/", true},
{"http://127.0.0.1/", true},
{"http://duckduckgo.com/?q=%2F", true},
{"http://localhost:3000/", true},
{"http://foobar.com/?foo=bar#baz=qux", true},
{"http://foobar.com?foo=bar", true},
{"http://www.xn--froschgrn-x9a.net/", true},
{"", false},
{"xyz://foobar.com", true},
{"invalid.", false},
{".com", false},
{"rtmp://foobar.com", true},
{"http://www.foo_bar.com/", true},
{"http://localhost:3000/", true},
{"http://foobar.com#baz=qux", true},
{"http://foobar.com/t$-_.+!*\\'(),", true},
{"http://www.foobar.com/~foobar", true},
{"http://www.-foobar.com/", true},
{"http://www.foo---bar.com/", true},
{"mailto:someone@example.com", true},
{"irc://irc.server.org/channel", true},
{"irc://#channel@network", true},
{"/abs/test/dir", true},
{"./rel/test/dir", false},
}
for _, test := range tests {
err := validator.ValidateFieldByTag(test.param, "uri")
if test.expected == true {
c.Assert(err, IsNil)
} else {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "uri")
}
}
}
func (ms *MySuite) TestOrTag(c *C) { func (ms *MySuite) TestOrTag(c *C) {
s := "rgba(0,31,255,0.5)" s := "rgba(0,31,255,0.5)"
err := validator.ValidateFieldByTag(s, "rgb|rgba") err := validator.ValidateFieldByTag(s, "rgb|rgba")

Loading…
Cancel
Save