forms

package module
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2020 License: MIT Imports: 11 Imported by: 0

README

Forms

GoDoc

Forms is a lightweight, but incredibly useful go library for parsing form data from an http.Request. It supports multipart forms, url-encoded forms, json data, and url query parameters. It also provides helper methods for converting data into other types and a Validator object which can be used to validate the data. Forms is framework-agnostic and works directly with the http package.

Version 0.4.0

Development Status

Forms is being actively developed and is well-tested. However, since it is still a young library, it is not recommended for use in mission-critical production applications at this time. It is probably fine to use for low-traffic hobby sites, and in fact we encourage its use in those settings to help polish the API and find missing features and hidden bugs. Pull requests and issue reports are much appreciated :)

Forms follows semantic versioning but offers no guarantees of backwards compatibility until version 1.0. Keep in mind that breaking changes might occur. We will do our best to make the community aware of any non-trivial breaking changes beforehand. We recommend using a dependency vendoring tool such as godep to ensure that breaking changes will not break your application.

Installation

Install like you would any other package:

go get github.com/albrow/forms

Then include the package in your import statements:

import "github.com/albrow/forms"

Example Usage

Meant to be used inside the body of an http.HandlerFunc or any function that has access to an http.Request.

func CreateUserHandler(res http.ResponseWriter, req *http.Request) {
	// Parse request data.
	userData, err := forms.Parse(req)
	if err != nil {
		// Handle err
		// ...
	}

	// Validate
	val := userData.Validator()
	val.Require("username")
	val.LengthRange("username", 4, 16)
	val.Require("email")
	val.MatchEmail("email")
	val.Require("password")
	val.MinLength("password", 8)
	val.Require("confirmPassword")
	val.Equal("password", "confirmPassword")
	val.RequireFile("profileImage")
	val.AcceptFileExts("profileImage", "jpg", "png", "gif")
	if val.HasErrors() {
		// Write the errors to the response
		// Maybe this means formatting the errors as json
		// or re-rendering the form with an error message
		// ...
	}

	// Use data to create a user object
	user := &models.User {
		Username: userData.Get("username"),
		Email: userData.Get("email"),
		HashedPassword: hash(userData.Get("password")),
	}

	// Continue by saving the user to the database and writing
	// to the response
	// ...


	// Get the contents of the profileImage file
	imageBytes, err := userData.GetFileBytes("profileImage")
	if err != nil {
	  // Handle err
	}
	// Now you can either copy the file over to your server using io.Copy,
	// upload the file to something like amazon S3, or do whatever you want
	// with it.
}

Contributing

See CONTRIBUTING.md

License

Forms is licensed under the MIT License. See the LICENSE file for more information.

Documentation

Overview

package forms is a lightweight, but incredibly useful library for parsing form data from an http.Request. It supports multipart forms, url-encoded forms, json data, and url query parameters. It also provides helper methods for converting data into other types and a Validator object which can be used to validate the data. Forms is framework-agnostic and works directly with the http package.

For the full source code, example usage, and more, visit https://github.com/albrow/forms.

Version 0.4.0

Index

Examples

Constants

View Source
const DefaultMaxFormSize = 500000

DefaultMaxFormSize is the default maximum form size (in bytes) used by the Parse function.

Variables

This section is empty.

Functions

This section is empty.

Types

type Data

type Data struct {
	// Values holds any basic key-value string data
	// This includes all fields from a json body or
	// urlencoded form, and the form fields only (not
	// files) from a multipart form
	Values url.Values
	// Files holds files from a multipart form only.
	// For any other type of request, it will always
	// be empty. Files only supports one file per key,
	// since this is by far the most common use. If you
	// need to have more than one file per key, parse the
	// files manually using req.MultipartForm.File.
	Files map[string]*multipart.FileHeader
	// contains filtered or unexported fields
}

Data holds data obtained from the request body and url query parameters. Because Data is built from multiple sources, sometimes there will be more than one value for a given key. You can use Get, Set, Add, and Del to access the first element for a given key or access the Values and Files properties directly to access additional elements for a given key. You can also use helper methods to convert the first value for a given key to a different type (e.g. bool or int).

func CreateFromMap

func CreateFromMap(m map[string]string) *Data

CreateFromMap returns a Data object with keys and values matching the map.

func Parse

func Parse(req *http.Request) (*Data, error)

Parse uses the default max form size defined above and calls ParseMax

Example
// Construct a request object for example purposes only.
// Typically you would be using this inside a http.HandlerFunc,
// not constructing your own request.
req, _ := http.NewRequest("GET", "/", nil)
values := url.Values{}
values.Add("name", "Bob")
values.Add("age", "25")
values.Add("retired", "false")
req.PostForm = values
req.Header.Set("Content-Type", "form-urlencoded")

// Parse the form data.
data, err := Parse(req)
if err != nil {
	panic(err)
}
name := data.Get("name")
age := data.GetInt("age")
retired := data.GetBool("retired")
if retired {
	fmt.Printf("%s is %d years old and he has retired.", name, age)
} else {
	fmt.Printf("%s is %d years old and not yet retired.", name, age)
}
Output:

Bob is 25 years old and not yet retired.

func ParseMax added in v0.3.4

func ParseMax(req *http.Request, max int64) (*Data, error)

ParseMax parses the request body and url query parameters into Data. The content in the body of the request has a higher priority, will be added to Data first, and will be the result of any operation which gets the first element for a given key (e.g. Get, GetInt, or GetBool).

func (*Data) Add

func (d *Data) Add(key string, value string)

Add adds the value to key. It appends to any existing values associated with key.

func (*Data) AddFile

func (d *Data) AddFile(key string, file *multipart.FileHeader)

AddFile adds the multipart form file to data with the given key.

func (Data) BindJSON added in v0.3.1

func (d Data) BindJSON(v interface{}) error

BindJSON binds v to the json data in the request body. It calls json.Unmarshal and sets the value of v.

func (*Data) Del

func (d *Data) Del(key string)

Del deletes the values associated with key.

func (*Data) DelFile

func (d *Data) DelFile(key string)

DelFile deletes the file associated with key (if any). If there is no file associated with key, it does nothing.

func (*Data) Encode

func (d *Data) Encode() string

Encode encodes the values into “URL encoded” form ("bar=baz&foo=quux") sorted by key. Any files in d will be ignored because there is no direct way to convert a file to a URL encoded value.

func (Data) FileExists

func (d Data) FileExists(key string) bool

FileExists returns true iff data.Files[key] exists. When parsing a request body, the key is considered to be in existence if it was provided in the request body, even if the file is empty.

func (Data) Get

func (d Data) Get(key string) string

Get gets the first value associated with the given key. If there are no values associated with the key, Get returns the empty string. To access multiple values, use the map directly.

func (Data) GetAndUnmarshalJSON

func (d Data) GetAndUnmarshalJSON(key string, v interface{}) error

GetAndUnmarshalJSON assumes that the first element in data[key] is a json string and attempts to unmarshal it into v. If unmarshaling was not successful, returns an error. v should be a pointer to some data structure.

func (Data) GetBool

func (d Data) GetBool(key string) bool

GetBool returns the first element in data[key] converted to a bool.

func (Data) GetBytes

func (d Data) GetBytes(key string) []byte

GetBytes returns the first element in data[key] converted to a slice of bytes.

func (Data) GetFile

func (d Data) GetFile(key string) *multipart.FileHeader

GetFile returns the multipart form file associated with key, if any, as a *multipart.FileHeader. If there is no file associated with key, it returns nil. If you just want the body of the file, use GetFileBytes.

func (Data) GetFileBytes

func (d Data) GetFileBytes(key string) ([]byte, error)

GetFileBytes returns the body of the file associated with key. If there is no file associated with key, it returns nil (not an error). It may return an error if there was a problem reading the file. If you need to know whether or not the file exists (i.e. whether it was provided in the request), use the FileExists method.

func (Data) GetFileReader added in v0.3.4

func (d Data) GetFileReader(key string) (io.Reader, error)

func (Data) GetFloat

func (d Data) GetFloat(key string) float64

GetFloat returns the first element in data[key] converted to a float.

func (Data) GetInt

func (d Data) GetInt(key string) int

GetInt returns the first element in data[key] converted to an int.

func (Data) GetMapFromJSON

func (d Data) GetMapFromJSON(key string) (map[string]interface{}, error)

GetMapFromJSON assumes that the first element in data[key] is a json string, attempts to unmarshal it into a map[string]interface{}, and if successful, returns the result. If unmarshaling was not successful, returns an error.

func (Data) GetSliceFromJSON

func (d Data) GetSliceFromJSON(key string) ([]interface{}, error)

GetSliceFromJSON assumes that the first element in data[key] is a json string, attempts to unmarshal it into a []interface{}, and if successful, returns the result. If unmarshaling was not successful, returns an error.

func (Data) GetStringsSplit

func (d Data) GetStringsSplit(key string, delim string) []string

GetStringsSplit returns the first element in data[key] split into a slice delimited by delim.

func (Data) KeyExists

func (d Data) KeyExists(key string) bool

KeyExists returns true iff data.Values[key] exists. When parsing a request body, the key is considered to be in existence if it was provided in the request body, even if its value is empty.

func (*Data) Set

func (d *Data) Set(key string, value string)

Set sets the key to value. It replaces any existing values.

func (*Data) Validator

func (d *Data) Validator() *Validator

Validator returns a Validator which can be used to easily validate data.

type ValidationResult

type ValidationResult struct {
	Ok bool
	// contains filtered or unexported fields
}

ValidationResult is returned from every validation method and can be used to override the default field name or error message. If you want to use the default fields and messages, simply discard the ValidationResult.

func (*ValidationResult) Field

func (vr *ValidationResult) Field(field string) *ValidationResult

Field changes the field name associated with the validation result.

func (*ValidationResult) Message

func (vr *ValidationResult) Message(msg string) *ValidationResult

Message changes the error message associated with the validation result. msg should typically be a user-readable sentence, such as "username is required."

type Validator

type Validator struct {
	// contains filtered or unexported fields
}

Validator has methods for validating its underlying Data. A Validator stores any errors that occurred during validation, and they can be accessed directly. In a typical workflow, you will create a Validator from some Data, call some methods on that validator (e.g. Require), check if the validator has errors, then do something with the errors if it does.

Example
// Construct a request object for example purposes only.
// Typically you would be using this inside a http.HandlerFunc,
// not constructing your own request.
req, _ := http.NewRequest("GET", "/", nil)
values := url.Values{}
values.Add("name", "Bob")
values.Add("age", "25")
req.PostForm = values
req.Header.Set("Content-Type", "form-urlencoded")

// Parse the form data.
data, _ := Parse(req)

// Validate the data.
val := data.Validator()
val.Require("name")
val.MinLength("name", 4)
val.Require("age")

// Here's how you can change the error message or field name
val.Require("retired").Field("retired_status").Message("Must specify whether or not person is retired.")

// Check for validation errors and print them if there are any.
if val.HasErrors() {
	fmt.Printf("%#v\n", val.Messages())
}
Output:

[]string{"name must be at least 4 characters long.", "Must specify whether or not person is retired."}

func (*Validator) AcceptFileExts

func (v *Validator) AcceptFileExts(field string, exts ...string) *ValidationResult

AcceptFileExts will add an error to the Validator if the extension of the file identified by field is not in exts. exts should be one ore more allowed file extensions, not including the preceding ".". If the file does not exist, it does not add an error to the Validator.

func (*Validator) AddError

func (v *Validator) AddError(field string, msg string) *ValidationResult

AddError adds an error associated with field to the validator. msg should typically be a user-readable sentence, such as "username is required."

func (*Validator) Equal

func (v *Validator) Equal(field1 string, field2 string) *ValidationResult

Equal will add an error to the Validator if data[field1] is not equal to data[field2].

func (*Validator) ErrorMap

func (v *Validator) ErrorMap() map[string][]string

ErrorMap reutrns all the fields and error messages for the validator in the form of a map. The keys of the map are field names, and the values are any error messages associated with that field name.

func (*Validator) Fields

func (v *Validator) Fields() []string

Fields returns the fields for all validation results for the Validator, in order.

func (*Validator) Greater

func (v *Validator) Greater(field string, value float64) *ValidationResult

Greater will add an error to the Validator if the first element of data.Values[field] is not greater than value or if the first element of data.Values[field] cannot be converted to a number.

func (*Validator) GreaterOrEqual

func (v *Validator) GreaterOrEqual(field string, value float64) *ValidationResult

GreaterOrEqual will add an error to the Validator if the first element of data.Values[field] is not greater than or equal to value or if the first element of data.Values[field] cannot be converted to a number.

func (*Validator) HasErrors

func (v *Validator) HasErrors() bool

HasErrors returns true iff the Validator has errors, i.e. if any validation methods called on the Validator failed.

func (*Validator) LengthRange

func (v *Validator) LengthRange(field string, min int, max int) *ValidationResult

LengthRange will add an error to the Validator if data.Values[field] is shorter than min (if data.Values[field] has less than min characters) or if data.Values[field] is longer than max (if data.Values[field] has more than max characters), not counting leading or trailing whitespace.

func (*Validator) Less

func (v *Validator) Less(field string, value float64) *ValidationResult

Less will add an error to the Validator if the first element of data.Values[field] is not less than value or if the first element of data.Values[field] cannot be converted to a number.

func (*Validator) LessOrEqual

func (v *Validator) LessOrEqual(field string, value float64) *ValidationResult

LessOrEqual will add an error to the Validator if the first element of data.Values[field] is not less than or equal to value or if the first element of data.Values[field] cannot be converted to a number.

func (*Validator) Match

func (v *Validator) Match(field string, regex *regexp.Regexp) *ValidationResult

Match will add an error to the Validator if data.Values[field] does not match the regular expression regex.

func (*Validator) MatchEmail

func (v *Validator) MatchEmail(field string) *ValidationResult

MatchEmail will add an error to the Validator if data.Values[field] does not match the formatting expected of an email.

func (*Validator) MaxLength

func (v *Validator) MaxLength(field string, length int) *ValidationResult

MaxLength will add an error to the Validator if data.Values[field] is longer than length (if data.Values[field] has more than length characters), not counting leading or trailing whitespace.

func (*Validator) Messages

func (v *Validator) Messages() []string

Messages returns the messages for all validation results for the Validator, in order.

func (*Validator) MinLength

func (v *Validator) MinLength(field string, length int) *ValidationResult

MinLength will add an error to the Validator if data.Values[field] is shorter than length (if data.Values[field] has less than length characters), not counting leading or trailing whitespace.

func (*Validator) Require

func (v *Validator) Require(field string) *ValidationResult

Require will add an error to the Validator if data.Values[field] does not exist, is an empty string, or consists of only whitespace.

func (*Validator) RequireFile

func (v *Validator) RequireFile(field string) *ValidationResult

RequireFile will add an error to the Validator if data.Files[field] does not exist or is an empty file

func (*Validator) TypeBool

func (v *Validator) TypeBool(field string) *ValidationResult

TypeBool will add an error to the Validator if the first element of data.Values[field] cannot be converted to a bool.

func (*Validator) TypeFloat

func (v *Validator) TypeFloat(field string) *ValidationResult

TypeFloat will add an error to the Validator if the first element of data.Values[field] cannot be converted to an float64.

func (*Validator) TypeInt

func (v *Validator) TypeInt(field string) *ValidationResult

TypeInt will add an error to the Validator if the first element of data.Values[field] cannot be converted to an int.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL