reqtruct

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2019 License: BSD-3-Clause Imports: 12 Imported by: 0

README

Reqtruct

Reqtruct is a Go package to simplify the extraction and conversion of parameters from a http.Request

It borrows some code from gorilla/schema.

It discovers your parameters based on the definitions you provide in struct tags and looks for them in headers, query params, path params, form, files or JSON.

If given no specification for where to look for params it will use the provided default location, if none is provided then it falls back to JSON.

Example

First we define the structs to hold the data.

type Image struct {
	ID      uuid.UUID             // type uuid.UUID implements TextUnmarshaller, location will be form
	Image   *multipart.FileHeader // uploaded file will be here
	Caption string                // location will form because of custom defaults
}

// AddImagesRequest is the struct that will hold the data for a AddImages request
type AddImagesRequest struct {
	Images        []*Image `name:"imgs"`   // provide custom alias name
	UserID        int      `from:"path"`   // decoder will look for UserID in path params
	Authorization string   `from:"header"` // decoder will get Authorization header
}

type Pagination struct {
	Limit  int
	Offset int
	Since  *Time
}

// GetImagesRequest is the struct that will hold the data for a GetImages request
type GetImagesRequest struct {
	// embedded structs pass their locations to their fields
	// only if no location is defined for the fields
	Pagination    `from:"query"`
	UserID        int    `from:"path"`
	Authorization string `from:"header,query"` // location will be either header or query
}

// Time is added to show how you can define a custom type for params
type Time struct {
	time.Time
}

const ctLayout = "2006/01/02|15:04:05"

func (t *Time) UnmarshalText(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "" {
		t.Time = time.Time{}
		return
	}
	t.Time, err = time.Parse(ctLayout, s)
	return
}

func (t *Time) String() string {
	return t.Format(ctLayout)
}

Then in the main function we set up the decoder

d := reqtruct.NewDecoder()
d.Separator('[', ']', 0) // paths will be in the format a[b][c][0][]...
d.NameFunc(func(name string, _ []int) string {
	// special function to convert a field name to its alias
	// only when no alias is provided
	// in this case it converts to snake case
	return ToSnakeCase(name)
})
d.DefaultLocation(reqtruct.LocationForm)
d.PathExtractor(func(r *http.Request) map[string]string {
	// function to extract path params from the router
	// in this case it is for julienschmidt/httprouter
	m := map[string]string{}
	params := httprouter.ParamsFromContext(r.Context())
	for _, p := range params {
		m[p.Key] = p.Value
	}
	return m
})

And finally we set up the router and use the decoder in the handlers

r := httprouter.New()

r.Handler("POST", "/users/:user_id/images", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	req := &AddImagesRequest{}
	err := d.Decode(req, r)
	if err != nil {
		fmt.Fprintln(w, err)
		return
	}
	for _, img := range req.Images {
		if img.Image == nil {
			continue
		}
		fmt.Fprintln(w, req.UserID, req.Authorization, img.ID, img.Image.Filename, img.Caption)
	}
}))

r.Handler("GET", "/users/:user_id/images", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	req := &GetImagesRequest{}
	err := d.Decode(req, r)
	if err != nil {
		fmt.Fprintln(w, err)
		return
	}

	fmt.Fprintln(w, req.UserID, req.Authorization, req.Limit, req.Offset, req.Since)
}))

http.ListenAndServe(":8080", r)

Call GET http://localhost:8080/users/1/images?offset=100&limit=10&since=2006/01/02|15:04:05&authorization=token

Or call POST http://localhost:8080/users/1/images with the following params in the body as a multipart/form-data:

imgs[0][id]=5c33d4c9-a943-4a0b-a55b-82365daca7de
imgs[0][image]=@<first_file_path>
imgs[0][caption]=caption one
imgs[1][id]=56239022-f25b-4b35-9c1b-346c5d4767f9
imgs[1][image]=@<second_file_path>
imgs[1][caption]=caption two

Documentation

Index

Constants

View Source
const (
	LocationPath int
	LocationQuery
	LocationHeader
	LocationForm
	LocationFile
	LocationJSON
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ContentTypeError

type ContentTypeError struct {
	ContentType        string
	RequestContentType string
}

func (ContentTypeError) Error

func (e ContentTypeError) Error() string

type ConversionError

type ConversionError struct {
	Key   string       // key from the source map.
	Type  reflect.Type // expected type of elem
	Index int          // index for multi-value fields; -1 for single-value fields.
	Err   error        // low-level error (when it exists)
}

ConversionError stores information about a failed conversion.

func (ConversionError) Error

func (e ConversionError) Error() string

func (ConversionError) Unwrap

func (e ConversionError) Unwrap() error

type Converter

type Converter func(string) reflect.Value

type Decoder

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

Decoder decodes params from a *http.Request to a struct.

func NewDecoder

func NewDecoder() *Decoder

NewDecoder returns a new Decoder.

func (*Decoder) CollectErrors

func (d *Decoder) CollectErrors(c bool)

CollectErrors specifies whether to return on the first error or accumulate errors.

func (*Decoder) Decode

func (d *Decoder) Decode(dst interface{}, r *http.Request) error

Decode decodes a *http.Request to a struct.

The first parameter must be a pointer to a struct. The second parameter is a pointer to http.Request.

func (*Decoder) DefaultLocation

func (d *Decoder) DefaultLocation(l int)

DefaultLocation sets the default location to look for params. It is only applied if a field does not have location tags.

func (*Decoder) IgnoreUnknownKeys

func (d *Decoder) IgnoreUnknownKeys(i bool)

IgnoreUnknownKeys controls the behaviour when the decoder encounters unknown keys in the map. If i is true and an unknown field is encountered, it is ignored. This is similar to how unknown keys are handled by encoding/json. If i is false then Decode will return an error. Note that any valid keys will still be decoded in to the target struct.

func (*Decoder) MaxMemory

func (d *Decoder) MaxMemory(m int64)

Max memory sets the max memory used when parsing multipart forms

func (*Decoder) NameFunc

func (d *Decoder) NameFunc(n func(field string, locations []int) string)

NameFunc allows settings a special function for getting fields aliases

func (*Decoder) PathExtractor

func (d *Decoder) PathExtractor(p func(r *http.Request) map[string]string)

PathExtractor defines the mechanism to extract path params from URIs. It takes a function that takes a *http.Request and returns map[string]string

func (*Decoder) RegisterConverter

func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter)

RegisterConverter registers a converter function for a custom type.

func (*Decoder) Separator

func (d *Decoder) Separator(left rune, right rune, sep rune)

Separator defines runes to be used as separators. If given '[', ']', '.' for example, then paths should be like a[b].[0].[c] This is provided to make it possible to accept serialized objects from jQuery for example. Both left and right can be set to 0 if only changing the separator

func (*Decoder) ZeroEmpty

func (d *Decoder) ZeroEmpty(z bool)

ZeroEmpty controls the behaviour when the decoder encounters empty values in a map. If z is true and a key in the map has the empty string as a value then the corresponding struct field is set to the zero value. If z is false then empty strings are ignored.

The default value is false, that is empty values do not change the value of the struct field.

type LocationError

type LocationError struct {
	Key              string
	AllowedLocations []int
	Location         int
}

func (LocationError) Error

func (e LocationError) Error() string

type MultiError

type MultiError map[string]error

MultiError stores multiple decoding errors.

Borrowed from the App Engine SDK.

func (MultiError) Error

func (e MultiError) Error() string

type ParsingError

type ParsingError struct {
	WrappedErr error
	Err        error
}

func (ParsingError) Error

func (e ParsingError) Error() string

func (ParsingError) Unwrap

func (e ParsingError) Unwrap() error

type UnknownKeyError

type UnknownKeyError struct {
	Key string // key from the source map.
}

UnknownKeyError stores information about an unknown key in the source map.

func (UnknownKeyError) Error

func (e UnknownKeyError) Error() string

Jump to

Keyboard shortcuts

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