zvalidate – zgo.at/zvalidate Index | Examples | Files | Directories

package zvalidate

import "zgo.at/zvalidate"

Package zvalidate provides simple validation for Go.

See the README.md for an introduction.

Example

Code:play 

package main

import (
	"fmt"

	"zgo.at/zvalidate"
)

func main() {
	email := "martin@arp42.net"

	v := zvalidate.New()
	v.Required("email", email)
	m := v.Email("email", email)

	if v.HasErrors() {
		fmt.Printf("Had the following validation errors:\n%s", v)
	}

	fmt.Printf("parsed email: %s\n", m.Address)

}

Output:

parsed email: martin@arp42.net

Index

Examples

Variables

var (
	AlphaNumeric = &unicode.RangeTable{
		R16:         []unicode.Range16{{0x0030, 0x0039, 1}, {0x0041, 0x005a, 1}, {0x0061, 0x007a, 1}},
		LatinOffset: 3,
	}
	ASCII = &unicode.RangeTable{
		R16:         []unicode.Range16{{0x0020, 0x007e, 1}},
		LatinOffset: 1,
	}
)

Range tables for Contains()

TODO: move to zstd/zunicode?

var DefaultMessages = Messages{
	Required:           func() string { return "must be set" },
	Domain:             func() string { return "must be a valid domain" },
	Hostname:           func() string { return "must be a valid hostname" },
	URL:                func() string { return "must be a valid url" },
	Email:              func() string { return "must be a valid email address" },
	IPv4:               func() string { return "must be a valid IPv4 address" },
	IP:                 func() string { return "must be a valid IPv4 or IPv6 address" },
	HexColor:           func() string { return "must be a valid color code" },
	LenLonger:          func() string { return "must be longer than %d characters" },
	LenShorter:         func() string { return "must be shorter than %d characters" },
	Exclude:            func() string { return "cannot be ‘%s’" },
	Include:            func() string { return "must be one of ‘%s’" },
	Integer:            func() string { return "must be a whole number" },
	Hex:                func() string { return "must be a whole number in base 16 (hexadecimal)" },
	Octal:              func() string { return "must be a whole number in base 8 (octal)" },
	Bool:               func() string { return "must be a boolean" },
	Date:               func() string { return "must be a date as ‘%s’" },
	Phone:              func() string { return "must be a valid phone number" },
	PhoneInternational: func() string { return "must be a valid phone number with country dialing prefix" },
	RangeHigher:        func() string { return "must be %d or higher" },
	RangeLower:         func() string { return "must be %d or lower" },
	UTF8:               func() string { return "must be UTF-8" },
	Contains:           func() string { return "cannot contain the characters %s" },
}

Functions

func TemplateError

func TemplateError(k string, v *Validator) template.HTML

TemplateError displays validation errors for the given key.

This will Pop() errors and modify the Validator in-place, so we can see if there are any "hidden" errors later on.

Example

Code:play 

package main

import (
	"html/template"
	"os"

	"zgo.at/zvalidate"
)

func main() {
	funcs := template.FuncMap{
		"validate":   zvalidate.TemplateError,
		"has_errors": zvalidate.TemplateHasErrors,
	}

	t := template.Must(template.New("").Funcs(funcs).Parse(`
<input name="xxx">
{{validate "xxx" .Validate}}

{{if has_errors .Validate}}Hidden: {{.Validate.HTML}}{{end}}
	`))

	v := zvalidate.New()
	v.Append("xxx", "oh noes")
	v.Append("hidden", "sneaky")

	t.Execute(os.Stdout, map[string]any{
		"Validate": &v,
	})

}

Output:

<input name="xxx">
<span class="err">Error: oh noes</span>

Hidden: <ul class='zvalidate'>
<li><strong>hidden</strong>: sneaky.</li>
</ul>

func TemplateHasErrors

func TemplateHasErrors(v *Validator) bool

TemplateHasErrors reports if there are any validation errors.

This is useful because "and" evaluates all arguments, and this will error out:

{{if and .Validate .Validate.HasErrors}}

Types

type Messages

type Messages struct {
	Required           func() string
	Domain             func() string
	Hostname           func() string
	URL                func() string
	Email              func() string
	IPv4               func() string
	IP                 func() string
	HexColor           func() string
	LenLonger          func() string
	LenShorter         func() string
	Exclude            func() string
	Include            func() string
	Integer            func() string
	Hex                func() string
	Octal              func() string
	Bool               func() string
	Date               func() string
	Phone              func() string
	PhoneInternational func() string
	RangeHigher        func() string
	RangeLower         func() string
	UTF8               func() string
	Contains           func() string
}

type Validator

type Validator struct {
	Errors map[string][]string `json:"errors"`
	// contains filtered or unexported fields
}

Validator hold the validation errors.

Typically you shouldn't create this directly but use the New() function.

func As

func As(err error) *Validator

As tries to convert this error to a Validator, returning nil if it's not.

func New

func New() Validator

New initializes a new Validator.

func (*Validator) Append

func (v *Validator) Append(key, value string, format ...any)

Append a new error.

func (*Validator) Boolean

func (v *Validator) Boolean(key, value string, message ...string) bool

Boolean parses as string as a boolean.

func (Validator) Code

func (v Validator) Code() int

Code returns the HTTP status code for the error. Satisfies the guru.coder interface in zgo.at/guru.

func (*Validator) Contains

func (v *Validator) Contains(key, value string, ranges []*unicode.RangeTable, runes []rune, message ...string)

Contains validates that this string only contains the given characters.

This implies the UTF8() validation.

The value is in either the unicode range or runes list. For example:

zvalidate.Contains("key", val, zvalidate.AlphaNumeric, []rune{'_', '-'})

Will allow all ASCII letters and numbers and '_' and '-'.

If you have a lot of values it's faster to create a custom RangeTable.

Useful ranges:

zvalidate.AlphaNumeric    a-0A-Za-z
zvalidate.ASCII           All ASCII characters except control characters.
unicode.Letter            Any "letter" (in any script)
unicode.Number            Any "number" (in any script)
unicode.ASCII_Hex_Digit   0-9A-Fa-f

func (*Validator) Date

func (v *Validator) Date(key, value, layout string, message ...string) time.Time

Date parses a string in the given date layout.

func (*Validator) Domain

func (v *Validator) Domain(key, value string, message ...string) []string

Domain parses a domain as individual labels.

A domain must consist of at least two labels. So "com" or "localhost" – while technically valid domain names – are not accepted, whereas "example.com" or "me.localhost" are. For the overwhelming majority of applications this makes the most sense.

This works for internationalized domain names (IDN), either as UTF-8 characters or as punycode.

func (*Validator) Email

func (v *Validator) Email(key, value string, message ...string) mail.Address

Email parses an email address.

func (Validator) Error

func (v Validator) Error() string

Error interface.

func (Validator) ErrorJSON

func (v Validator) ErrorJSON() ([]byte, error)

ErrorJSON for reporting errors as JSON.

func (*Validator) ErrorOrNil

func (v *Validator) ErrorOrNil() error

ErrorOrNil returns nil if there are no errors, or the Validator object if there are.

This makes it a bit more elegant to return from a function:

if v.HasErrors() {
    return v
}
return nil

Can now be:

return v.ErrorOrNil()

func (*Validator) Exclude

func (v *Validator) Exclude(key, value string, exclude []string, message ...string) string

Exclude validates that the value is not in the exclude list.

This list is matched case-insensitive and with leading/trailing whitespace ignored. The returned value is the same as the input value.

func (*Validator) HTML

func (v *Validator) HTML() template.HTML

HTML representation of all errors, or a blank string if there are none.

func (*Validator) HasErrors

func (v *Validator) HasErrors() bool

HasErrors reports if this validation has any errors.

func (*Validator) Hex

func (v *Validator) Hex(key, value string, message ...string) int64

Hex parses a string as a base-16 integer.

func (*Validator) HexColor

func (v *Validator) HexColor(key, value string, message ...string) (uint8, uint8, uint8)

HexColor parses a color as a hex triplet (e.g. #ffffff or #fff).

func (*Validator) Hostname

func (v *Validator) Hostname(key, value string, message ...string) []string

Hostname checks if this is a valid hostname.

This is different from Domain in that it considers any hostname valid, whereas Domain() is a bit stricter and validates if this is likely to be a publicly accessible domain. e.g. "localhost" is valid in Hostname(), but not Domain().

func (*Validator) IP

func (v *Validator) IP(key, value string, message ...string) net.IP

IP parses an IPv4 or IPv6 address.

func (*Validator) IPv4

func (v *Validator) IPv4(key, value string, message ...string) net.IP

IPv4 parses an IPv4 address.

func (*Validator) Include

func (v *Validator) Include(key string, value, include any, message ...string) any

Include validates that the value is in the include list.

This list is matched case-insensitive and with leading/trailing whitespace ignored. The returned value is the "sanitized" value that matched.

func (*Validator) Integer

func (v *Validator) Integer(key, value string, message ...string) int64

Integer parses a string as an integer.

func (*Validator) Len

func (v *Validator) Len(key, value string, min, max int, message ...string) int

Len validates the character (rune) length of a string.

A maximum of 0 indicates there is no upper limit.

func (*Validator) Merge

func (v *Validator) Merge(other Validator)

Merge errors from another validator in to this one.

func (*Validator) Messages

func (v *Validator) Messages(m Messages)

Messages sets the messages to use for validation errors.

func (*Validator) Octal

func (v *Validator) Octal(key, value string, message ...string) int64

Octal parses a string as a base-8 integer.

func (*Validator) Phone

func (v *Validator) Phone(key, value string, message ...string) string

Phone parses a phone number.

There are a great amount of writing conventions for phone numbers: https://en.wikipedia.org/wiki/National_conventions_for_writing_telephone_numbers

This merely checks a field contains 5 to 20 characters "0123456789+\-() .", which is not very strict but should cover all conventions.

Returns the phone number with grouping/spacing characters removed.

func (*Validator) PhoneInternational

func (v *Validator) PhoneInternational(key, value string, message ...string) string

PhoneInternational parses a phone number.

This is identical to Phone(), except that a phone must start with a "+", requiring a country prefix (Phone() will deal with country prefixes as well, it just isn't required).

Returns the phone number with grouping/spacing characters removed.

func (*Validator) Pop

func (v *Validator) Pop(key string) []string

Pop an error, removing all errors for this key.

This is mostly useful when displaying errors next to forms: Pop() all the errors you want to display, and then display anything that's left with a flash message or the like. This prevents "hidden" errors.

Returns nil if there are no errors for this key.

func (*Validator) Range

func (v *Validator) Range(key string, value, min, max int64, message ...string)

Range sets the minimum and maximum value of a integer.

A maximum of 0 indicates there is no upper limit.

func (*Validator) Required

func (v *Validator) Required(key string, value any, message ...string)

Required validates that the value is not the type's zero value.

For slices it also checks it's not a slice of zero values.

func (*Validator) String

func (v *Validator) String() string

Strings representation of all errors, or a blank string if there are none.

func (*Validator) Sub

func (v *Validator) Sub(key, subKey string, err error)

Sub adds sub-validations.

Errors from the subvalidation are merged with the top-level one, the keys are added as "top.sub" or "top[n].sub".

If the error is not a Validator the text will be added as just the key name without subkey (i.e. the same as v.Append("key", "msg")).

For example:

func (c Customer) validateSettings() error {
    v := zvalidate.New()
    v.Required("domain", c.Domain)
    v.Required("email", c.Email)
    return v.ErrorOrNil()
}

v := zvalidate.New()
v.Required("name", customer.Name)

// Keys will be added as "settings.domain" and "settings.email".
v.Sub("settings", "", customer.validateSettings())

// List as array; keys will be added as "addresses[0].city" etc.
for i, addr := range customer.Addresses {
    v.Sub("addresses", i, addr.Validate())
}

func (*Validator) URL

func (v *Validator) URL(key, value string, message ...string) *url.URL

URL parses an URL.

The URL may consist of a scheme, host, path, and query parameters. Only the host is required.

Local URLs are not considered valid; the host needs to have at least two labels. Use URLLocal() if you also want to accept e.g "http://localhost".

If the scheme is not given "http" will be prepended.

func (*Validator) URLLocal

func (v *Validator) URLLocal(key, value string, message ...string) *url.URL

URLLocal is like URL, but also considers local URLs to be valid.

func (*Validator) UTF8

func (v *Validator) UTF8(key, value string, message ...string)

UTF8 validates that this string is valid UTF-8.

Caveat: this will consider NULL bytes *invalid* even though they're valid in UTF-8. Many tools don't accept it (e.g. PostgreSQL and SQLite), there's very rarely a reason to include them in strings, and most uses I've seen is from people trying to insert exploits. So the practical thing to do is just to reject it.

Source Files

messages.go punycode.go template.go utf8.go validators.go zvalidate.go

Directories

PathSynopsis
internal
Version
v0.0.0-20241009015312-efd45e4f063d (latest)
Published
Oct 9, 2024
Platform
linux/amd64
Imports
16 packages
Last checked
3 weeks ago

Tools for package owners.