spinneret

package module
v0.0.0-...-d9f8047 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2020 License: MIT Imports: 11 Imported by: 0

README

Spinneret

Spinneret is a collection of utility functions to aid in the development of web applications in Go. this builds on top of some packages provided by the Gorilla web toolkit such as gorilla/schema and gorilla/sessions.

This package provides the ability to easily handle form validation, file uploads, serving different content types, and flashing of form data between requests.

Examples

Form Validation

Form validation is achieved via the spinneret.Form method that wraps the Fields, and Validate methods. The Validate method is what is called to actually perform the form validation, and the Fields method is what's called when form data is flashed to the session. Below is an example of a form implementation,

type Login struct {
    Email    string `schema:"email"`
    Password string `schema:"password"`
}

func (f Login) Fields() map[string]string {
    return map[string]string{
        "email": f.Email,
    }
}

func (f Login) Validate() error {
    errs := spinneret.NewErrors()

    if f.Email == "" {
        errs.Put("email", spinneret.ErrFieldRequired("email"))
    }

    if f.Password == "" {
        errs.Put("password", spinneret.ErrFieldRequired("password"))
    }
    return errs.Err()
}

the gorilla/schema package is used to handle the unmarshalling of request data into a form.

Each implementation of the spinneret.Form interface should return the *spinneret.Errors type containg any validation errors that occur. If any other errors occur during the invocation of Validate, (such as a database connection error), then it is fine to return these directly.

File Uploads

File uploads can be handled via the spinneret.File type that can be created via spinneret.NewFile. Below is an example of handling file uploads, elided for brevity,

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

func Upload(w http.ResponseWriter, r *http.Request) {
   sess, _ := store.Get(r, "session")

    f := spinneret.NewFile("avatar", 5 * (1 << 20), w, r)
    f.Allow("image/png", "image/jpeg")

    if err := spinneret.UnmarshalAndValidate(f, r); err != nil {
        errs, ok := err.(*spinneret.Errors)

        if ok {
            spinneret.FlashFormWithErrors(sess, f, errs)
            sess.Save(r, w)
            http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
            return
        }
        panic(errs) // don't actually do this
    }

    dir, _ := os.Getwd()
    dst, _ := ioutil.TempFile(dir, "")

    // Store the file on disk.
    io.Copy(dst, f)

    w.WriteHeader(http.StatusOK)
}

we specify that a file upload is going to take place via the NewFile function, this will return *spinneret.File for handling the upload and validation of files. The Allow method is then called to tell it that we only want to allow files with the given MIME types. Finally we then pass this to UnmarshalAndValidate. This is the function that actually parses the request data and validates it. If any validation errors do occur, then *spinneret.Errors will be returned. We then flash this information to the session, and redirect back.

Response Types

HTML, Text, and JSON response types can be sent using the respective functions provided by this package. These functions will set the appropriate Content-Type header, and Content-Length too.

func HTMLHandler(w http.ResponseWriter, r *http.Request) {
    spinneret.HTML(w, "<h1>HTML response</h1>", http.StatusOK)
}

func TextHandler(w http.ResponseWriter, r *http.Request) {
    spinneret.Text(w, "Text response", http.StatusOK)
}

func JSONHandler(w http.ResponseWriter, r *http.Request) {
    data := map[string]string{
        "message": "JSON response",
    }

    spinneret.JSON(w, data, http.StatusOK)
}

Documentation

Overview

Package spinneret provides a set of utility functions and types to aid in web development in Go.

This package provides the ability to easily handle form validation, file uploads, serving different content types, and flashing of form data between requests.

Form validation is achieved via the `spinneret.Form` method that wraps the `Fields`, and `Validate` methods. The `Validate` method is what is called to actually perform the form validation, and the `Fields` method is what's called when form data is flashed to the session. Below is an example of a form implementation,

type Login struct {
    Email    string `schema:"email"`
    Password string `schema:"password"`
}

func (f Login) Fields() map[string]string {
    return map[string]string{
        "email": f.Email,
    }
}

func (f Login) Validate() error {
    errs := spinneret.NewErrors()

    if f.Email == "" {
        errs.Put("email", spinneret.ErrFieldRequired("email"))
    }

    if f.Password == "" {
        errs.Put("password", spinneret.ErrFieldRequired("password"))
    }
    return errs.Err()
}

Each implementation of the `spinneret.Form` interface should return the `*spinneret.Errors` type containg any validation errors that occur. If any other errors occur during the invocation of `Validate`, (such as a database connection error), then it is fine to return these directly.

File uploads can be handled via the `spinneret.File` type that can be created via `spinneret.NewFile`. Below is an example of handling file uploads, elided for brevity,

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

func Upload(w http.ResponseWriter, r *http.Request) {
   sess, _ := store.Get(r, "session")

    f := spinneret.NewFile("avatar", 5 * (1 << 20), w, r)
    f.Allow("image/png", "image/jpeg")

    if err := spinneret.UnmarshalAndValidate(f, r); err != nil {
        errs, ok := err.(*spinneret.Errors)

        if ok {
            spinneret.FlashFormWithErrors(sess, f, errs)
            sess.Save(r, w)
            http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
            return
        }
        panic(errs) // don't actually do this
    }

    dir, _ := os.Getwd()
    dst, _ := ioutil.TempFile(dir, "")

    // Store the file on disk.
    io.Copy(dst, f)

    w.WriteHeader(http.StatusOK)
}

we specify that a file upload is going to take place via the `NewFile` function, this will return `*spinneret.File` for handling the upload and validation of files. The `Allow` method is then called to tell it that we only want to allow files with the given MIME types. Finally we then pass this to `UnmarshalAndValidate`. This is the function that actually parses the request data and validates it. If any validation errors do occur, then `*spinneret.Errors` will be returned. We then flash this information to the session, and redirect back.

HTML, Text, and JSON response types can be sent using the respective functions provided by this package. These functions will set the appropriate `Content-Type` header, and `Content-Length` too.

func HTMLHandler(w http.ResponseWriter, r *http.Request) {
    spinneret.HTML(w, "<h1>HTML response</h1>", http.StatusOK)
}

func TextHandler(w http.ResponseWriter, r *http.Request) {
    spinneret.Text(w, "Text response", http.StatusOK)
}

func JSONHandler(w http.ResponseWriter, r *http.Request) {
    data := map[string]string{
        "message": "JSON response",
    }

    spinneret.JSON(w, data, http.StatusOK)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BaseAddress

func BaseAddress(r *http.Request) string

BaseAddress will return the HTTP address for the given Request. This will return the Scheme of the current Request (http, or https), concatenated with the host. If the X-Forwarded-Proto, and X-Forwarded-Host headers are present in the Request, then they will be used for the Scheme and Host respectively.

func BasePath

func BasePath(path string) string

BasePath returns the last element of the given path. This will split the path using the "/" spearator. If the path is empty BasePath returns "/".

func ErrField

func ErrField(field string, err error) error

ErrField records the given error for the given field.

func ErrFieldExists

func ErrFieldExists(field string) error

ErrFieldExists records an error should the given field's value already exist, for example an email in a database.

func ErrFieldRequired

func ErrFieldRequired(field string) error

ErrFieldRequired records an error for a field that was not provided in a form.

func FlashFormWithErrors

func FlashFormWithErrors(sess *sessions.Session, f Form, errs *Errors)

FlashFormWithErrors flashes the given Form and Errors to the given session under the "form_fields" and "form_errors" keys respectively.

func FormFields

func FormFields(sess *sessions.Session) map[string]string

FormField returns the map of form fields that has been flashed to the given session under the "form_fields" key. If the key does not exist, then an empty map is returned instead.

func HTML

func HTML(w http.ResponseWriter, content string, status int)

HTML sets the Content-Type of the given ResponseWriter to text/html, and writes the given content with the given status code to the writer. This will also set the Content-Length header to the len of content.

func JSON

func JSON(w http.ResponseWriter, data interface{}, status int)

JSON sets the Content-Type of the given ResponseWriter to application/json, and encodes the given interface to JSON to the given writer, with the given status code. This will also set the Content-Length header to the len of the JSON encoded data.

func Text

func Text(w http.ResponseWriter, content string, status int)

Text sets the Content-Type of the given ResponseWriter to text/plain, and writes the given content with the given status code to the writer. This will also se the Content-Length header to the len of content.

func Unmarshal

func Unmarshal(f Form, r *http.Request) error

Unmarshal will decode the contents of the given request into the given Form. If the request is of application/json then the entire body is decoded as JSON into the Form, otherwise this function assumes a typical form submission.

func UnmarshalAndValidate

func UnmarshalAndValidate(f Form, r *http.Request) error

UnmarshalAndValidate will unmarshal the given request into the given Form. and validate it. If an underlying schema.MultiError, or UnmarshalError occurs during unmarshalling then a validation attempt is still made.

Types

type Errors

type Errors map[string][]string

Errors records the errors that occur during form validation. Each key is a field within the form that erred, and the values are the list of error messages.

func FormErrors

func FormErrors(sess *sessions.Session) *Errors

FormErrors returns the Errors that has been flashed to the given session under the "form_errors" key. If the key does not exist, then an empty Errors is returned instead.

func NewErrors

func NewErrors() *Errors

NewErrors returns an empty set of Errors.

func (*Errors) Err

func (e *Errors) Err() error

Err returns the underlying error for the current set of Errors. If there are no errors recorded, then this returns nil.

func (*Errors) Error

func (e *Errors) Error() string

Error builds a formatted string of the errors in the set, the final string is formatted like so,

field:
    error

func (*Errors) First

func (e *Errors) First(key string) string

First returns the first error message that can be found for the given field. If no message can be found then an empty string is returned.

func (*Errors) Merge

func (e *Errors) Merge(e1 *Errors)

Merge merges the set of errors from e1 into the given set.

func (*Errors) Put

func (e *Errors) Put(key string, err error)

Put appends the given error message to the given key in the set.

type File

type File struct {
	multipart.File

	// Type is the MIME of the file being uploaded.
	Type string

	// Request is the current HTTP request through which the file is being
	// uploaded.
	Request *http.Request
	// contains filtered or unexported fields
}

File provides an implementation of the Form interface to validation file uploads via HTTP. It embeds the underlying multiepart.File type from the stdlib.

func NewFile

func NewFile(field string, size int64, w http.ResponseWriter, r *http.Request) *File

NewFile returns a new File for the given form field with the given maximum file size. If size is 0 then no limit is set on the size of a file that can be uploaded.

func (*File) Allow

func (f *File) Allow(mimes ...string)

Allow specifies a list of mimes we want to allow during file upload. This will rever any preceding calls to Disallowed.

func (*File) Disallow

func (f *File) Disallow(mimes ...string)

Disallow specifies a list of mimes we want to allow during file upload. This will revert any preceding calls to Allowed.

func (*File) Fields

func (*File) Fields() map[string]string

Fields will always return nil.

func (*File) Validate

func (f *File) Validate() error

Validate will check the size of the file being uploaded, and set the Type of the file. If any mimes have been set then these will be checked to determine of the Type of the file is allowed or disallowed. If the request the file was sent over is anything over than multipart/form-data, then the entire request body is treated as the file contents itself.

type Form

type Form interface {
	// Fields returns a map of all the fields in the form, and their string
	// values.
	Fields() map[string]string

	// Validate the given form and return any errors that occur. If validation
	// fails then it is expected for the returned error to be of the type
	// Errors.
	Validate() error
}

Form wraps the Fields and Validate methods for representing data that is being POSTed to an HTTP server for validation.

type UnmarshalError

type UnmarshalError struct {
	Field string
	Err   error
}

UnmarshalError records the error that occurred during the unmarshalling of a field in a form.

func (UnmarshalError) Error

func (e UnmarshalError) Error() string

Error returns the formatted string of the UnmarshalError.

Jump to

Keyboard shortcuts

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