binder

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Dec 4, 2024 License: MIT Imports: 13 Imported by: 0

README

GoDoc GitHub release GitHub license

HTTP BINDER

Golang net/http compatible request binder!

This is a modified Echo http binder to bind structs and maps from the net/http requests

Parsing request data is a crucial part of a web application, this process is usually called binding, and it'is done with information passed by the client in the following parts of an HTTP request:

  • URL Path parameter
  • URL Query parameter
  • Header
  • Request body

This package provides different ways to "bind" request data to go types.

Installation

To install Binder, use go get:

go get github.com/gobigbang/binder

Struct Tag Binding

With struct binding you define a Go struct with tags specifying the data source and corresponding key. In your request handler you simply call binder.BindHttp(r *http.Request, i interface{}) with a pointer to your struct. The tags tell the binder everything it needs to know to load data from the request.

In this example a struct type User tells the binder to bind the query string parameter id to its string field ID:

type User struct {
  ID string `query:"id"`
}

// bind this route to something like /user?id=abc
func(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := binder.BindHttp(r, &user); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // do something with the user
}

Data Sources

The binder supports the following struct tags to bind the source:

  • query - query parameter
  • param - path parameter (also called route)
  • header - header parameter
  • json - request body. Uses builtin Go json package for unmarshalling.
  • xml - request body. Uses builtin Go xml package for unmarshalling.
  • form - form data. Values are taken from query and request body. Uses Go standard library form parsing.

You can modify the tag binding name on the binder instance.

Data Types

When decoding the request body, the following data types are supported as specified by the Content-Type header:

  • application/json
  • application/xml
  • application/x-www-form-urlencoded
  • multipart/form-data

When binding path parameter, query parameter, header, or form data, tags must be explicitly set on each struct field. However, JSON and XML binding is done on the struct field name if the tag is omitted. This is according to the behavior of Go's json package.

For form data, the package parses form data from both the request URL and body if content type is not MIMEMultipartForm. See documentation for non-MIMEMultipartFormand MIMEMultipartForm

Multiple Sources

It is possible to specify multiple sources on the same field. In this case request data is bound in this order (by default):

  1. Path parameters
  2. Query parameters
  3. Request body
type User struct {
  ID string `param:"id" query:"id" form:"id" json:"id" xml:"id"`
}

Note that binding at each stage will overwrite data bound in a previous stage. This means if your JSON request contains the query param name=query and body {"name": "body"} then the result will be User{Name: "body"}.

[!NOTE] Please note that BindHeaders is not enabled by default, you must enable it manually or call binder.BindHeader specifically.

Security

To keep your application secure, avoid passing bound structs directly to other methods if these structs contain fields that should not be bindable. It is advisable to have a separate struct for binding and map it explicitly to your business struct.

Consider what will happen if your bound struct has an Exported field IsAdmin bool and the request body contains {IsAdmin: true, Name: "hacker"}.

Example

In this example we define a User struct type with field tags to bind from json, form, or query request data:

type UserDTO struct {
  Name  string `json:"name" form:"name" query:"name"`
  Email string `json:"email" form:"email" query:"email"`
}

type User struct {
  Name    string
  Email   string
  IsAdmin bool
}

And a handler at the POST /users route binds request data to the struct:

func(w http.ResponseWriter, r *http.Request) {
  u := new(UserDTO)
  if err := binder.BindHttp(r, u); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
  }

  // Load into separate struct for security
  user := User{
    Name: u.Name,
    Email: u.Email,
    IsAdmin: false // avoids exposing field that should not be bound
  }

  executeSomeBusinessLogic(user)

  // return something
}
JSON Data
curl -X POST http://localhost:1323/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Joe","email":"joe@labstack"}'
Form Data
curl -X POST http://localhost:1323/users \
  -d 'name=Joe' \
  -d 'email=joe@labstack.com'
Query Parameters
curl -X GET 'http://localhost:1323/users?name=Joe&email=joe@labstack.com'
Supported Data Types
Data Type Notes
bool
float32
float64
int
int8
int16
int32
int64
uint
uint8/byte Does not support bytes(). Use BindUnmarshaler/CustomFunc to convert value from base64 etc to []byte{}.
uint16
uint32
uint64
string
time
duration
BindUnmarshaler() binds to a type implementing BindUnmarshaler interface
TextUnmarshaler() binds to a type implementing encoding.TextUnmarshaler interface
JsonUnmarshaler() binds to a type implementing json.Unmarshaler interface
UnixTime() converts Unix time (integer) to time.Time
UnixTimeMilli() converts Unix time with millisecond precision (integer) to time.Time
UnixTimeNano() converts Unix time with nanosecond precision (integer) to time.Time
CustomFunc() callback function for your custom conversion logic

Each supported type has the following methods:

  • <Type>("param", &destination) - if parameter value exists then binds it to given destination of that type i.e Int64(...).
  • Must<Type>("param", &destination) - parameter value is required to exist, binds it to given destination of that type i.e MustInt64(...).
  • <Type>s("param", &destination) - (for slices) if parameter values exists then binds it to given destination of that type i.e Int64s(...).
  • Must<Type>s("param", &destination) - (for slices) parameter value is required to exist, binds it to given destination of that type i.e MustInt64s(...).

For certain slice types BindWithDelimiter("param", &dest, ",") supports splitting parameter values before type conversion is done. For example binding an integer slice from the URL /api/search?id=1,2,3&id=1 will result in []int64{1,2,3,1}.

License

MIT License

Documentation

Index

Constants

View Source
const (
	CONNECT = http.MethodConnect
	DELETE  = http.MethodDelete
	GET     = http.MethodGet
	HEAD    = http.MethodHead
	OPTIONS = http.MethodOptions
	PATCH   = http.MethodPatch
	POST    = http.MethodPost
	// PROPFIND = "PROPFIND"
	PUT   = http.MethodPut
	TRACE = http.MethodTrace
)

HTTP methods NOTE: Deprecated, please use the stdlib constants directly instead.

View Source
const (
	// MIMEApplicationJSON JavaScript Object Notation (JSON) https://www.rfc-editor.org/rfc/rfc8259
	MIMEApplicationJSON = "application/json"
	// Deprecated: Please use MIMEApplicationJSON instead. JSON should be encoded using UTF-8 by default.
	// No "charset" parameter is defined for this registration.
	// Adding one really has no effect on compliant recipients.
	// See RFC 8259, section 8.1. https://datatracker.ietf.org/doc/html/rfc8259#section-8.1
	MIMEApplicationJSONCharsetUTF8       = MIMEApplicationJSON + "; " + charsetUTF8
	MIMEApplicationJavaScript            = "application/javascript"
	MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
	MIMEApplicationXML                   = "application/xml"
	MIMEApplicationXMLCharsetUTF8        = MIMEApplicationXML + "; " + charsetUTF8
	MIMETextXML                          = "text/xml"
	MIMETextXMLCharsetUTF8               = MIMETextXML + "; " + charsetUTF8
	MIMEApplicationForm                  = "application/x-www-form-urlencoded"
	MIMEApplicationProtobuf              = "application/protobuf"
	MIMEApplicationMsgpack               = "application/msgpack"
	MIMETextHTML                         = "text/html"
	MIMETextHTMLCharsetUTF8              = MIMETextHTML + "; " + charsetUTF8
	MIMETextPlain                        = "text/plain"
	MIMETextPlainCharsetUTF8             = MIMETextPlain + "; " + charsetUTF8
	MIMEMultipartForm                    = "multipart/form-data"
	MIMEOctetStream                      = "application/octet-stream"
)

MIME types

View Source
const (

	// PROPFIND Method can be used on collection and property resources.
	PROPFIND = "PROPFIND"
	// REPORT Method can be used to get information about a resource, see rfc 3253
	REPORT = "REPORT"
	// RouteNotFound is special method type for routes handling "route not found" (404) cases
	RouteNotFound = "echo_route_not_found"
)
View Source
const (
	HeaderAccept         = "Accept"
	HeaderAcceptEncoding = "Accept-Encoding"
	// HeaderAllow is the name of the "Allow" header field used to list the set of methods
	// advertised as supported by the target resource. Returning an Allow header is mandatory
	// for status 405 (method not found) and useful for the OPTIONS method in responses.
	// See RFC 7231: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
	HeaderAllow               = "Allow"
	HeaderAuthorization       = "Authorization"
	HeaderContentDisposition  = "Content-Disposition"
	HeaderContentEncoding     = "Content-Encoding"
	HeaderContentLength       = "Content-Length"
	HeaderContentType         = "Content-Type"
	HeaderCookie              = "Cookie"
	HeaderSetCookie           = "Set-Cookie"
	HeaderIfModifiedSince     = "If-Modified-Since"
	HeaderLastModified        = "Last-Modified"
	HeaderLocation            = "Location"
	HeaderRetryAfter          = "Retry-After"
	HeaderUpgrade             = "Upgrade"
	HeaderVary                = "Vary"
	HeaderWWWAuthenticate     = "WWW-Authenticate"
	HeaderXForwardedFor       = "X-Forwarded-For"
	HeaderXForwardedProto     = "X-Forwarded-Proto"
	HeaderXForwardedProtocol  = "X-Forwarded-Protocol"
	HeaderXForwardedSsl       = "X-Forwarded-Ssl"
	HeaderXUrlScheme          = "X-Url-Scheme"
	HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
	HeaderXRealIP             = "X-Real-Ip"
	HeaderXRequestID          = "X-Request-Id"
	HeaderXCorrelationID      = "X-Correlation-Id"
	HeaderXRequestedWith      = "X-Requested-With"
	HeaderServer              = "Server"
	HeaderOrigin              = "Origin"
	HeaderCacheControl        = "Cache-Control"
	HeaderConnection          = "Connection"

	// Access control
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"

	// Security
	HeaderStrictTransportSecurity         = "Strict-Transport-Security"
	HeaderXContentTypeOptions             = "X-Content-Type-Options"
	HeaderXXSSProtection                  = "X-XSS-Protection"
	HeaderXFrameOptions                   = "X-Frame-Options"
	HeaderContentSecurityPolicy           = "Content-Security-Policy"
	HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
	HeaderXCSRFToken                      = "X-CSRF-Token"
	HeaderReferrerPolicy                  = "Referrer-Policy"
)

Headers

Variables

View Source
var ArrayMatcherRegexp = regexp.MustCompile(`\[([0-9]+)\]`) // matches [0] to use in indexed arrays
View Source
var ArrayNotationRegexp = regexp.MustCompile(`\[([a-zA-Z0-9\-\_\.]+)\]`) // matches [id] to use in deep objects
View Source
var DefaultBodySize = int64(32 << 20) // 32 MB
View Source
var DefaultDeepObjectSeparator = "." // default separator for deep fields
View Source
var DefaultFormTagName = "form" // default tag name for form
View Source
var DefaultHeaderTagName = "header" // default tag name for header
View Source
var DefaultParamTagName = "param" // default tag name for param
View Source
var DefaultQueryTagName = "query" // default tag name for query
View Source
var MapMatcherRegexp = regexp.MustCompile(`\[([a-zA-Z0-9\-\_\.]+)\]`) // matches [key] to use in maps and deep objects
View Source
var MaxArraySize = 1000 // max size of array
View Source
var PathMatcherRegexp = regexp.MustCompile(`\{([^}]+)\}`) // matches {id} to use in path parameters

Functions

func Bind added in v0.0.2

func Bind(r BindableRequest, i interface{}) error

func BindBody added in v0.0.2

func BindBody(r BindableRequest, i interface{}) error

func BindHeaders added in v0.0.2

func BindHeaders(r BindableRequest, i interface{}) error

func BindHttp added in v0.0.2

func BindHttp(r *http.Request, i interface{}) error

BindHttp binds an http.Request to a struct or map.

func BindHttpBody added in v0.0.2

func BindHttpBody(r *http.Request, i interface{}) error

BindHttpBody binds an http.Request body to a struct or map.

func BindHttpHeaders added in v0.0.2

func BindHttpHeaders(r *http.Request, i interface{}) error

func BindHttpPathParms added in v0.0.2

func BindHttpPathParms(r *http.Request, i interface{}) error

func BindHttpQueryParams added in v0.0.2

func BindHttpQueryParams(r *http.Request, i interface{}) error

func BindPathParams added in v0.0.2

func BindPathParams(r BindableRequest, i interface{}) error

func BindQueryParams added in v0.0.2

func BindQueryParams(r BindableRequest, i interface{}) error

Types

type BindFunc added in v0.0.2

type BindFunc func(r BindableRequest, i interface{}) error

type BindUnmarshaler

type BindUnmarshaler interface {
	// UnmarshalParam decodes and assigns a value from an form or query param.
	UnmarshalParam(param string) error
}

BindUnmarshaler is the interface used to wrap the UnmarshalParam method. Types that don't implement this, but do implement encoding.TextUnmarshaler will use that interface instead.

type BindableRequest added in v0.0.2

type BindableRequest interface {
	GetBody() io.Reader
	GetPathPattern() string
	GetPathValue(string) string
	GetQuery() url.Values
	GetHeaders() url.Values
	GetContentLength() int64
	GetContentType() string
	GetForm() (url.Values, error)
	GetMultipartForm(maxBodySize int64) (*multipart.Form, error)
}

BindableRequest is the interface that wraps the basic methods required for a request to be bindable.

This enables non-HTTP request types to be bindable.

type Binder

type Binder interface {
	Bind(r BindableRequest, i interface{}) error
	BindBody(r BindableRequest, i interface{}) error
	BindPathParams(r BindableRequest, i interface{}) error
	BindQueryParams(r BindableRequest, i interface{}) error
	BindHeaders(r BindableRequest, i interface{}) error
}
var DefaultBinderInstance Binder

func GetBinder added in v0.0.2

func GetBinder() Binder

Returns the default binder instance.

type DefaultBinder

type DefaultBinder struct {
	JSONSerializer       JSONSerializer
	XMLSerializer        XMLSerializer
	PathMatcher          *regexp.Regexp
	ArrayMatcher         *regexp.Regexp
	MapMatcher           *regexp.Regexp
	ArrayNotationMatcher *regexp.Regexp
	DeepObjectSeparator  string
	MaxBodySize          int64
	MaxArraySize         int
	HeaderTagName        string
	FormTagName          string
	QueryTagName         string
	ParamTagName         string
	BindOrder            []BindFunc
}

DefaultBinder is the default implementation of the `Binder` interface.

func NewBinder

func NewBinder() *DefaultBinder

func (*DefaultBinder) Bind

func (b *DefaultBinder) Bind(r BindableRequest, i interface{}) (err error)

Bind implements the `Binder#Bind` function. Binding is done in following order: 1) path params; 2) query params; 3) request body. Each step COULD override previous step binded values. For single source binding use their own methods BindBody, BindQueryParams, BindPathParams.

func (*DefaultBinder) BindBody

func (b *DefaultBinder) BindBody(r BindableRequest, i interface{}) (err error)

BindBody binds request body contents to bindable object NB: then binding forms take note that this implementation uses standard library form parsing which parses form data from BOTH URL and BODY if content type is not MIMEMultipartForm See non-MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseForm See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm

func (*DefaultBinder) BindHeaders

func (b *DefaultBinder) BindHeaders(r BindableRequest, i interface{}) error

BindHeaders binds HTTP headers to a bindable object

func (*DefaultBinder) BindPathParams

func (b *DefaultBinder) BindPathParams(r BindableRequest, i interface{}) error

BindPathParams binds path params to bindable object

func (*DefaultBinder) BindQueryParams

func (b *DefaultBinder) BindQueryParams(r BindableRequest, i interface{}) error

BindQueryParams binds query params to bindable object

func (*DefaultBinder) GetHeaders added in v0.0.2

func (b *DefaultBinder) GetHeaders(r BindableRequest) map[string][]string

func (*DefaultBinder) GetPathParams added in v0.0.2

func (b *DefaultBinder) GetPathParams(r BindableRequest) map[string][]string

func (*DefaultBinder) GetQueryParams added in v0.0.2

func (b *DefaultBinder) GetQueryParams(r BindableRequest) map[string][]string

type DefaultJSONSerializer

type DefaultJSONSerializer struct{}

func (DefaultJSONSerializer) Deserialize

func (DefaultJSONSerializer) Deserialize(r BindableRequest, i interface{}) error

type DefaultXMLSerializer

type DefaultXMLSerializer struct{}

func (DefaultXMLSerializer) Deserialize

func (DefaultXMLSerializer) Deserialize(r BindableRequest, i interface{}) error

type HttpBindableRequest added in v0.0.2

type HttpBindableRequest struct {
	*http.Request
}

func NewHttpBindableRequest added in v0.0.2

func NewHttpBindableRequest(r *http.Request) HttpBindableRequest

func (HttpBindableRequest) GetBody added in v0.0.2

func (r HttpBindableRequest) GetBody() io.Reader

func (HttpBindableRequest) GetContentLength added in v0.0.2

func (r HttpBindableRequest) GetContentLength() int64

func (HttpBindableRequest) GetContentType added in v0.0.2

func (r HttpBindableRequest) GetContentType() string

func (HttpBindableRequest) GetForm added in v0.0.2

func (r HttpBindableRequest) GetForm() (url.Values, error)

func (HttpBindableRequest) GetHeaders added in v0.0.2

func (r HttpBindableRequest) GetHeaders() url.Values

func (HttpBindableRequest) GetMultipartForm added in v0.0.2

func (r HttpBindableRequest) GetMultipartForm(maxBodySize int64) (*multipart.Form, error)

func (HttpBindableRequest) GetPathPattern added in v0.0.2

func (r HttpBindableRequest) GetPathPattern() string

func (HttpBindableRequest) GetPathValue added in v0.0.2

func (r HttpBindableRequest) GetPathValue(key string) string

func (HttpBindableRequest) GetQuery added in v0.0.2

func (r HttpBindableRequest) GetQuery() url.Values

type HttpBinder added in v0.0.2

type HttpBinder struct {
	Binder
}
var DefaultHttpBinder *HttpBinder

func GetHttpBinder added in v0.0.2

func GetHttpBinder() *HttpBinder

func NewHttpBinder added in v0.0.2

func NewHttpBinder() *HttpBinder

func (*HttpBinder) Bind added in v0.0.2

func (b *HttpBinder) Bind(r *http.Request, i interface{}) error

func (*HttpBinder) BindBody added in v0.0.2

func (b *HttpBinder) BindBody(r *http.Request, i interface{}) error

func (*HttpBinder) BindHeaders added in v0.0.2

func (b *HttpBinder) BindHeaders(r *http.Request, i interface{}) error

func (*HttpBinder) BindPathParams added in v0.0.2

func (b *HttpBinder) BindPathParams(r *http.Request, i interface{}) error

func (*HttpBinder) BindQueryParams added in v0.0.2

func (b *HttpBinder) BindQueryParams(r *http.Request, i interface{}) error

type JSONSerializer

type JSONSerializer interface {
	// Serialize(c context.Context, i interface{}, indent string) error
	Deserialize(r BindableRequest, i interface{}) error
}

JSONSerializer is the interface that encodes and decodes JSON to and from interfaces.

type XMLSerializer

type XMLSerializer interface {
	Deserialize(r BindableRequest, i interface{}) error
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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