Not sure how to structure your Go web application?

My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.

Min and max length (number of characters)

If you have the HTML form:

<input type="text" name="foo">

You can verify that the "foo" field contains a certain number of characters with the utf8.RuneCountInString function. This is subtly different to checking the number of bytes. For example, the string "Zoë" contains 3 characters but is 4 bytes long because of the accented character.

Starts with, ends with and contains

If you have the HTML form:

<input type="text" name="foo">

You can verify that the "foo" field starts with, ends with, or contains a particular string using the functions in the strings package:

import "strings"
···
// Check that the field value starts with 'abc'.
if !strings.HasPrefix(r.Form.Get("foo"), "abc") {
fmt.Println("error: foo does not start with 'abc'")
}
// Check that the field value ends with 'abc'.
if !strings.HasSuffix(r.Form.Get("foo"), "abc") {
fmt.Println("error: foo does not end with 'abc'")
}
// Check that the field value contains 'abc' anywhere in it.
if !strings.Contains(r.Form.Get("foo"), "abc") {
fmt.Println("error: foo does not contain 'abc'")
}

Matches regular expression pattern

If you have the HTML form:

<input type="text" name="foo">

You can verify that the "foo" field matches a particular regular expression using the regexp package. For example, to check that it matches the pattern ^[a-z]{4}\.[0-9]{2}$ (four lowercase letters followed by a period and two digits):

import "regexp"
···
// Pre-compiling the regular expression and storing it in a variable is more efficient
// if you're going to use it multiple times. The regexp.MustCompile function will
// panic on failure.
var rxPat = regexp.MustCompile(`^[a-z]{4}.[0-9]{2}$`)
if !rxPat.MatchString(r.Form.Get("foo")) {
fmt.Println("error: foo does not match the required pattern")
}

Note that because the dot character has a special meaning in regular expressions, we escaped it using the \ character so it is interpreted as a literal period character instead. In the example above we also used a raw string for the regular expression. If you use an interpreted string (i.e. a string surrounded by double quotes), you need to escape the backslash too because that's the escape character for interpreted strings. So you would need to write:

var rxPat = regexp.MustCompile("^[a-z]{4}\.[0-9]{2}$")

If you're not familiar with regular expressions then this guide from Mozilla is a good explanation.

Unicode character range

If you have the HTML form:

<input type="text" name="foo">

You can verify that the "foo" field only contains characters in a certain unicode range using the regexp package. For example, to check that it contains only Cyrillic characters in the two unicode blocks 0400 - 04FF (Cyrillic) and 0500 - 052F (Cyrillic Supplementary):

import "regexp"
···
// Use an interpreted string and the \u escape notation to create a regular
// expression matching the range of characters in the two unicode code blocks.
var rxCyrillic = regexp.MustCompile("^[\u0400-\u04FF\u0500-\u052F]+$")
if !rxCyrillic.MatchString(r.Form.Get("foo")) {
fmt.Println("error: foo must only contain Cyrillic characters")
}

Email validation

If you have the HTML form:

<input type="email" name="foo">

You can sanity check that the "foo" field contains an email address using the regexp package. Choosing a regular expression to use for email validation is a contentious topic, but as a starting point I would suggest the pattern recommended by the W3C and Web Hypertext Application Technology Working Group.

Note that we have to use an interpreted string for the regular expression because it contains a backtick character (which means we can't use a raw string).

URL validation

If you have the HTML form:

<input type="url" name="foo">

You can verify that the "foo" field contains a valid URL by first parsing it with the url.Parse function. This will take a URL string, break it into it's component pieces, and store it as a url.URL struct. You can then sanity check the component pieces as necessary.

For instance, to check that a URL is absolute (i.e has both a scheme and host) and that the scheme is either http or https:

import "net/url"
···
// If there are any major problems with the format of the URL, url.Parse() will
// return an error.
u, err := url.Parse(r.Form.Get("foo"))
if err != nil {
fmt.Println("error: foo is not a valid URL")
} else if u.Scheme == "" || u.Host == "" {
fmt.Println("error: foo must be an absolute URL")
} else if u.Scheme != "http" && u.Scheme != "https" {
fmt.Println("error: foo must begin with http or https")
}

Integers

If you have the HTML form:

<input type="number" name="foo" min="0" max="100" step="5">

You can verify that the "foo" field contains an integer by parsing it with the strconv.Atoi function. You can then sanity check the integer as necessary.

For instance, to check that an integer is a multiple of 5 between 0 and 100:

Date

If you have the HTML form:

<input type="date" name="foo" min="2017-01-01" max="2017-12-31">

You can verify that the "foo" field contains a valid date by parsing it with the time.Parse function. It will return an error if the date is not real: any day of month larger than 31 is rejected, as is February 29 in non-leap years, February 30, February 31, April 31, June 31, September 31, and November 31.

If you're not familiar with time.Parse, it converts strings with a given format into a time.Time object. You specify what the format is by passing the reference time (Mon Jan 2 15:04:05 -0700 MST 2006) as the first parameter, laid-out in the format you want. If you are expecting a date in the format YYYY-MM-DD then the reference time is 2006-01-02.

You can then use the various functions in the time package to sanity check the date as necessary.

For instance, to check that an date is valid and between 2017-01-01 and 2017-12-31:

Datetime-local

You can verify that the "foo" field contains a valid datetime for a specific location by parsing it with the time.ParseInLocation function. This will return an error if the date is not real: any day of month larger than 31 is rejected, as is February 29 in non-leap years, February 30, February 31, April 31, June 31, September 31, and November 31.

For instance, to check that an datetime for the "Europe/Vienna" timezone is valid and between 2017-01-01 00:00 and 2017-12-31 23:59:

The time zone database needed by LoadLocation may not be present on all systems, especially non-Unix systems. LoadLocation looks in the directory or uncompressed zip file named by the ZONEINFO environment variable, if any, then looks in known installation locations on Unix systems, and finally looks in $GOROOT/lib/time/zoneinfo.zip.