binding

package
v0.0.0-...-8ce6181 Latest Latest
Warning

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

Go to latest
Published: Feb 8, 2014 License: MIT Imports: 6 Imported by: 34

README

binding

Request data binding for Martini.

API Reference

Description

Package binding provides several middleware for transforming raw request data into populated structs, validating the input, and handling the errors. Each handler is independent and optional.

Bind

binding.Bind is a convenient wrapper over the other handlers in this package. It does the following boilerplate for you:

  1. Deserializes the request data into a struct you supply
  2. Performs validation with binding.Validate
  3. Bails out with binding.ErrorHandler if there are any errors

Your application (the final handler) will not even see the request if there are any errors.

It reads the Content-Type of the request to know how to deserialize it, or if the Content-Type is not specified, it tries different deserializers until one returns without errors.

Important safety tip: Don't attempt to bind a pointer to a struct. This will cause a panic to prevent a race condition where every request would be pointing to the same struct.

Form

binding.Form deserializes form data from the request, whether in the query string or as a form-urlencoded payload, and puts the data into a struct you pass in. It then invokes the binding.Validate middleware to perform validation. No error handling is performed, but you can get the errors in your handler by receiving a binding.Errors type.

Json

binding.Json deserializes JSON data in the payload of the request and uses binding.Validate to perform validation. Similar to binding.Form, no error handling is performed, but you can get the errors and handle them yourself.

Validate

binding.Validate receives a populated struct and checks it for errors, first by enforcing the binding:"required" value on struct field tags, then by executing the Validate() method on the struct, if it is a binding.Validator. (See usage below for an example.)

Note: Marking a field as "required" means that you do not allow the zero value for that type (i.e. if you want to allow 0 in an int field, do not make it required).

ErrorHandler

binding.ErrorHandler is a small middleware that simply writes a 400 code to the response and also a JSON payload describing the errors, if any errors have been mapped to the context. It does nothing if there are no errors.

Usage

This is a contrived example to show a few different ways to use the binding package.

package main

import (
   "net/http"
   
   "github.com/codegangsta/martini"
   "github.com/codegangsta/martini-contrib/binding"
 )

type BlogPost struct {
	Title   string    `form:"title" json:"title" binding:"required"`
	Content string    `form:"content" json:"content"`
	Views   int       `form:"views" json:"views"`
	unexported string `form:"-"`  // skip binding of unexported fields
}

// This method implements binding.Validator and is executed by the binding.Validate middleware
func (bp BlogPost) Validate(errors *binding.Errors, req *http.Request) {
	if req.Header.Get("X-Custom-Thing") == "" {
		errors.Overall["x-custom-thing"] = "The X-Custom-Thing header is required"
	}
	if len(bp.Title) < 4 {
		errors.Fields["title"] = "Too short; minimum 4 characters"
	} else if len(bp.Title) > 120 {
		errors.Fields["title"] = "Too long; maximum 120 characters"
	}
	if bp.Views < 0 {
		errors.Fields["views"] = "Views must be at least 0"
	}
}

func main() {
	m := martini.Classic()

	m.Post("/blog", binding.Bind(BlogPost{}), func(blogpost BlogPost) string {
		// This function won't execute if there were errors
		return blogpost.Title
	})

	m.Get("/blog", binding.Form(BlogPost{}), binding.ErrorHandler, func(blogpost BlogPost) string {
		// This function won't execute if there were errors
		return blogpost.Title
	})

	m.Get("/blog", binding.Form(BlogPost{}), func(blogpost BlogPost, err binding.Errors, resp http.ResponseWriter) string {
		// This function WILL execute if there are errors because binding.Form doesn't handle errors
		if err.Count() > 0 {
			resp.WriteHeader(http.StatusBadRequest)
		}
		return blogpost.Title
	})

	m.Post("/blog", binding.Json(BlogPost{}), myOwnErrorHandler, func(blogpost BlogPost) string {
		// By this point, I assume that my own middleware took care of any errors
		return blogpost.Title
	})

	m.Run()
}

Authors

Documentation

Overview

Package binding transforms, with validation, a raw request into a populated structure used by your application logic.

Index

Constants

View Source
const (
	RequireError         string = "Required"
	DeserializationError string = "DeserializationError"
	IntegerTypeError     string = "IntegerTypeError"
	BooleanTypeError     string = "BooleanTypeError"
	FloatTypeError       string = "FloatTypeError"
)

Variables

View Source
var (
	// Maximum amount of memory to use when parsing a multipart form.
	// Set this to whatever value you prefer; default is 10 MB.
	MaxMemory = int64(1024 * 1024 * 10)
)

Functions

func Bind

func Bind(obj interface{}, ifacePtr ...interface{}) martini.Handler

Bind accepts a copy of an empty struct and populates it with values from the request (if deserialization is successful). It wraps up the functionality of the Form and Json middleware according to the Content-Type of the request, and it guesses if no Content-Type is specified. Bind invokes the ErrorHandler middleware to bail out if errors occurred. If you want to perform your own error handling, use Form or Json middleware directly. An interface pointer can be added as a second argument in order to map the struct to a specific interface.

func ErrorHandler

func ErrorHandler(errs Errors, resp http.ResponseWriter)

ErrorHandler simply counts the number of errors in the context and, if more than 0, writes a 400 Bad Request response and a JSON payload describing the errors with the "Content-Type" set to "application/json". Middleware remaining on the stack will not even see the request if, by this point, there are any errors. This is a "default" handler, of sorts, and you are welcome to use your own instead. The Bind middleware invokes this automatically for convenience.

func Form

func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler

Form is middleware to deserialize form-urlencoded data from the request. It gets data from the form-urlencoded body, if present, or from the query string. It uses the http.Request.ParseForm() method to perform deserialization, then reflection is used to map each field into the struct with the proper type. Structs with primitive slice types (bool, float, int, string) can support deserialization of repeated form keys, for example: key=val1&key=val2&key=val3 An interface pointer can be added as a second argument in order to map the struct to a specific interface.

func Json

func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler

Json is middleware to deserialize a JSON payload from the request into the struct that is passed in. The resulting struct is then validated, but no error handling is actually performed here. An interface pointer can be added as a second argument in order to map the struct to a specific interface.

func MultipartForm

func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Handler

func Validate

func Validate(obj interface{}) martini.Handler

Validate is middleware to enforce required fields. If the struct passed in is a Validator, then the user-defined Validate method is executed, and its errors are mapped to the context. This middleware performs no error handling: it merely detects them and maps them.

Types

type Errors

type Errors struct {
	Overall map[string]string `json:"overall"`
	Fields  map[string]string `json:"fields"`
}

Errors represents the contract of the response body when the binding step fails before getting to the application.

func (Errors) Count

func (self Errors) Count() int

Total errors is the sum of errors with the request overall and errors on individual fields.

type Validator

type Validator interface {
	Validate(*Errors, *http.Request)
}

Implement the Validator interface to define your own input validation before the request even gets to your application. The Validate method will be executed during the validation phase.

Jump to

Keyboard shortcuts

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