README
¶
Hyper Text Adaptors
Package htadaptor
provides convenient generic domain logic adaptors for HTTP handlers. It eliminates boiler plate code, increases security by enforcing read limits and struct validation, and reduces bugs by providing a more intuitive request data parsing API than the standard library.
Why do you need this package?
An HTTP request contains at least five various sources of input that your HTTP handlers may consider: URL path, URL query, headers, cookies, and the request body. Much of the code that you have to write manually is wrestling those inputs into a struct. Willem Schots wrote an excellent explanation here. htadaptor
can do all of it for you:
myHandler := htadaptor.Must(htadaptor.NewUnaryFuncAdaptor(
// your domain function call
func(context.Context, *myInputStruct) (*myOutputStruct, error) {
// ... myInputStruct is passed in already validated
// ... the fields of myInputStruct will be populated with
// ... the contents of `request.Body` with overrides
// from sources below in their given order:
},
htadaptor.WithPathValues("slug"), // (1) URL routing path
htadaptor.WithQueryValues("search"), // (2) URL query
htadaptor.WithHeaderValues("accessToken"), // (3) header
htadaptor.WithCookieValues("sessionID"), // (4) cookie
htadaptor.WithSessionValues("role"), // (5) session
))
The adaptors address common function signatures of domain logic calls that operate on a request struct and return a response struct with contextual awareness all the way through the call stack including the slog.Logger
:
Struct Adaptor | Parameter Values | Return Values |
---|---|---|
UnaryFunc | context, inputStruct | any, error |
NullaryFunc | context | any, error |
VoidFunc | context, inputStruct | error |
String adaptors are best when only one request value is needed:
String Adaptor | Parameter Values | Return Values |
---|---|---|
UnaryStringFunc | context, string | any, error |
VoidStringFunc | context, string | error |
Installation
go get github.com/dkotik/htadaptor@latest
Basic Usage
mux := http.NewServeMux()
mux.Handle("/api/v1/order", htadaptor.Must(
htadaptor.NewUnaryFuncAdaptor(myService.Order),
))
See examples folder for common project uses.
Adaptor Options
Extractors
The order of extractors matters with the latter overriding the former. Request body is always processed first.
- Path
- Chi Path
- Query
- Header
- Cookie
- Session
- Request properties can also be included into deserialization:
extract.NewMethodExtractor
extract.NewHostExtractor
extract.NewRemoteAddressExtractor
extract.NewUserAgentExtractor
- Or, make your own by implementing Extractor interface.
Credits
The core idea was sparked in conversations with members of the Ardan Labs team. Package includes reflection schema decoder from Gorilla toolkit. Similar projects:
- danielgtaylor/huma with REST and RPC
- dolanor/rip with REST
- go-fuego/fuego with OpenAPI
- matt1484/chimera for Chi
- calvinmclean/babyapi
How is htadaptor
different from the other generic HTTP adaptors? It is more terse due to focusing on wrapping http.Handlers
from the standard library. It is expected that the REST interface will be handled separately by either an http.Mux
or a helper.
Documentation
¶
Overview ¶
Package htadaptor provides generic domain logic adaptors for HTTP handlers. Adaptors come in three flavors:
- UnaryFunc: func(context, inputStruct) (outputStruct, error)
- NullaryFunc: func(context) (outputStruct, error)
- VoidFunc: func(context, inputStruct) error
Validation errors are decorated with the correct http.StatusUnprocessableEntity status code.
Index ¶
- Variables
- func ApplyMiddleware(h http.Handler, mws ...Middleware) http.Handler
- func ContextWithLanguage(parent context.Context, t language.Tag) context.Context
- func ContextWithLocalizer(parent context.Context, l *i18n.Localizer) context.Context
- func DefaultErrorTemplate() *template.Template
- func GetHyperTextStatusCode(err error) int
- func LanguageFromContext(ctx context.Context) language.Tag
- func LocalizerFromContext(ctx context.Context) (l *i18n.Localizer, ok bool)
- func Must(h http.Handler, err error) http.Handler
- func NewHostMux(hostHandlers ...HostMuxAssociation) (http.Handler, error)
- func NewMethodMux(ms *MethodSwitch) http.Handler
- func NewPermanentRedirect(to string) http.Handler
- func NewTemporaryRedirect(to string) http.Handler
- type Decoder
- type DecoderFunc
- type DecodingError
- type Deduplicator
- type Encoder
- type EncoderFunc
- type EncodingError
- type Error
- type ErrorHandler
- type ErrorHandlerFunc
- type ErrorMessage
- type FuncType
- type HostMuxAssociation
- type InvalidRequestError
- type Logger
- type LoggerFunc
- type MethodSwitch
- type Middleware
- type NotFoundError
- type NullaryFuncAdaptor
- type Option
- func WithCookieValues(names ...string) Option
- func WithDecoder(d Decoder) Option
- func WithDecoderOptions(withOptions ...reflectd.Option) Option
- func WithDefaultDecoder() Option
- func WithDefaultEncoder() Option
- func WithDefaultErrorHandler() Option
- func WithDefaultLogger() Option
- func WithDefaultStatusCode() Option
- func WithEncoder(e Encoder) Option
- func WithErrorHandler(h ErrorHandler) Option
- func WithExtractors(exs ...extract.RequestValueExtractor) Option
- func WithHeaderValues(names ...string) Option
- func WithLogger(l Logger) Option
- func WithMemoryLimit(upto int64) Option
- func WithOptions(withOptions ...Option) Option
- func WithPathValues(names ...string) Option
- func WithQueryValues(names ...string) Option
- func WithReadLimit(upto int64) Option
- func WithSessionValues(names ...string) Option
- func WithStatusCode(statusCode int) Option
- func WithTemplate(t *template.Template) Option
- type SlogLogger
- type UnaryFuncAdaptor
- type UnaryStringFuncAdaptor
- type Validatable
- type VoidFuncAdaptor
- type VoidStringFuncAdaptor
Constants ¶
This section is empty.
Variables ¶
var JSONEncoder = EncoderFunc( func(w http.ResponseWriter, r *http.Request, code int, v any) error { w.Header().Set("content-type", "application/json") w.WriteHeader(code) return json.NewEncoder(w).Encode(v) }, )
Functions ¶
func ApplyMiddleware ¶ added in v0.0.11
func ApplyMiddleware(h http.Handler, mws ...Middleware) http.Handler
Apply wraps an http.Handler into Middleware in reverse order.
func ContextWithLanguage ¶ added in v0.1.0
func ContextWithLocalizer ¶ added in v0.1.0
ContextWithLocalizer adds localizer into context as a value. Use LocalizerFromContext to recover it later.
func DefaultErrorTemplate ¶ added in v0.0.10
func GetHyperTextStatusCode ¶ added in v0.0.10
func LanguageFromContext ¶ added in v0.1.0
func LocalizerFromContext ¶ added in v0.1.0
LocalizerFromContext raises request-scoped localizer. Warning: localizer will be <nil> if it was not set using ContextWithLocalizer.
func Must ¶
Must panics if an http.Handler was created with an error.
func NewHostMux ¶ added in v0.1.3
func NewHostMux(hostHandlers ...HostMuxAssociation) (http.Handler, error)
NewHostMux creates an http.Handler that multiplexes by http.Request host name.
func NewMethodMux ¶ added in v0.1.3
func NewMethodMux(ms *MethodSwitch) http.Handler
NewMethodMux returns a handler that is able to satisfy REST interface expectations. It does not modify response status codes, but they can be updated using WithStatusCode option for individual handlers.
func NewPermanentRedirect ¶ added in v0.1.3
func NewTemporaryRedirect ¶ added in v0.1.3
Types ¶
type DecoderFunc ¶ added in v0.0.10
type DecodingError ¶ added in v0.0.10
type DecodingError struct {
// contains filtered or unexported fields
}
func (*DecodingError) Error ¶ added in v0.0.10
func (e *DecodingError) Error() string
func (*DecodingError) HyperTextStatusCode ¶ added in v0.0.10
func (e *DecodingError) HyperTextStatusCode() int
func (*DecodingError) Unwrap ¶ added in v0.0.10
func (e *DecodingError) Unwrap() error
type Deduplicator ¶ added in v0.1.1
type Deduplicator struct {
// contains filtered or unexported fields
}
Deduplicator tracks recent request structures. Use Deduplicator.IsDuplicate to determine if a request was seen in a certain time window.
Uses struct hashing by Mitchell Hashimoto. Using request deduplicator is superior to deduplicating HTTP requests, because they are large and can vary in myriads of ways. Struct deduplication can also be applied at network edge easing the pressure on the database or the event bus.
Handles most data structs. Cannot process functions inside structs.
func NewDeduplicator ¶ added in v0.1.1
func NewDeduplicator(ctx context.Context, window time.Duration) *Deduplicator
NewDeduplicator returns a new Deduplicator.
Context is used for the clean up go routine termination.
Window specifies the minimum duration of how long the duplicate tags are remembered for. Real duration can extend up to 50% longer because it depends on the clean up cycle.
func (*Deduplicator) IsDuplicate ¶ added in v0.1.1
func (d *Deduplicator) IsDuplicate(request any) (bool, error)
IsDuplicate returns true if the message hash tag calculated using a [MessageHasher] was seen in deduplication time window.
func (*Deduplicator) Len ¶ added in v0.1.1
func (d *Deduplicator) Len() (count int)
Len returns the number of known tags that have not been cleaned out yet.
type Encoder ¶
func NewPermanentRedirectEncoder ¶ added in v0.1.5
func NewPermanentRedirectEncoder() Encoder
NewPermanentRedirectEncoder redirects the HTTP client to the location returned by a domain call using http.StatusPermanentRedirect status.
If domain call does not return a string, returns an error.
func NewTemplateEncoder ¶ added in v0.0.11
func NewTemporaryRedirectEncoder ¶ added in v0.1.5
func NewTemporaryRedirectEncoder() Encoder
NewTemporaryRedirectEncoder redirects the HTTP client to the location returned by a domain call using http.StatusTemporaryRedirect status.
If domain call does not return a string, returns an error.
type EncoderFunc ¶
func (EncoderFunc) Encode ¶
func (f EncoderFunc) Encode(w http.ResponseWriter, r *http.Request, code int, v any) error
type EncodingError ¶ added in v0.0.10
type EncodingError struct {
// contains filtered or unexported fields
}
func (*EncodingError) Error ¶ added in v0.0.10
func (e *EncodingError) Error() string
func (*EncodingError) HyperTextStatusCode ¶ added in v0.0.10
func (e *EncodingError) HyperTextStatusCode() int
func (*EncodingError) Unwrap ¶ added in v0.0.10
func (e *EncodingError) Unwrap() error
type Error ¶
Error extends the [error] interface to include HTTP status code.
func NewDecodingError ¶ added in v0.0.10
TODO: deprecate - there is no benefit to wrapping the error!
func NewEncodingError ¶ added in v0.0.10
type ErrorHandler ¶
type ErrorHandlerFunc ¶
func NewErrorHandler ¶ added in v0.0.10
func NewErrorHandler(encoder Encoder) ErrorHandlerFunc
func NewErrorHandlerFromTemplate ¶ added in v0.0.10
func NewErrorHandlerFromTemplate(t *template.Template) ErrorHandlerFunc
func (ErrorHandlerFunc) HandleError ¶
func (e ErrorHandlerFunc) HandleError(w http.ResponseWriter, r *http.Request, err error) error
type ErrorMessage ¶ added in v0.0.10
type HostMuxAssociation ¶ added in v0.1.5
HostMuxAssociation assigns a handler to a host for NewHostMux initialization.
It is used instead of a `map[string]http.Handler` because the intended order of hosts is preserved in case the resulting handler uses a list implementation internally. Golang maps do not preserve the order of their keys or values.
type InvalidRequestError ¶
type InvalidRequestError struct {
// contains filtered or unexported fields
}
type LoggerFunc ¶ added in v0.0.10
func (LoggerFunc) LogRequest ¶ added in v0.0.10
func (f LoggerFunc) LogRequest(r *http.Request, err error)
type MethodSwitch ¶ added in v0.1.3
type MethodSwitch struct { Get http.Handler Post http.Handler Put http.Handler Patch http.Handler Delete http.Handler Head http.Handler }
MethodSwitch provides method selections for NewMethodMux.
func (*MethodSwitch) AllowedMethods ¶ added in v0.1.3
func (ms *MethodSwitch) AllowedMethods() (methods []string)
type Middleware ¶ added in v0.0.11
Middleware modifies an http.Handler.
type NotFoundError ¶ added in v0.0.3
type NotFoundError struct {
// contains filtered or unexported fields
}
func NewNotFoundError ¶ added in v0.0.10
func NewNotFoundError(p string) *NotFoundError
func (*NotFoundError) Error ¶ added in v0.0.3
func (e *NotFoundError) Error() string
func (*NotFoundError) HyperTextStatusCode ¶ added in v0.0.3
func (e *NotFoundError) HyperTextStatusCode() int
func (*NotFoundError) LogValue ¶ added in v0.0.3
func (e *NotFoundError) LogValue() slog.Value
type NullaryFuncAdaptor ¶
type NullaryFuncAdaptor[O any] struct { // contains filtered or unexported fields }
NullaryFuncAdaptor calls a domain function with no input and returns a response struct.
func NewNullaryFuncAdaptor ¶
func NewNullaryFuncAdaptor[O any]( domainCall func(context.Context) (O, error), withOptions ...Option, ) (*NullaryFuncAdaptor[O], error)
NewNullaryFuncAdaptor creates a new adaptor for a function that takes no input and returns a struct.
func (*NullaryFuncAdaptor[O]) ServeHTTP ¶
func (a *NullaryFuncAdaptor[O]) ServeHTTP( w http.ResponseWriter, r *http.Request, )
ServeHTTP satisfies http.Handler interface.
type Option ¶
type Option func(*options) error
func WithCookieValues ¶ added in v0.0.6
func WithDecoder ¶
func WithDecoderOptions ¶
func WithDefaultDecoder ¶
func WithDefaultDecoder() Option
func WithDefaultEncoder ¶
func WithDefaultEncoder() Option
func WithDefaultErrorHandler ¶
func WithDefaultErrorHandler() Option
func WithDefaultLogger ¶
func WithDefaultLogger() Option
func WithDefaultStatusCode ¶ added in v0.1.5
func WithDefaultStatusCode() Option
func WithEncoder ¶
func WithErrorHandler ¶
func WithErrorHandler(h ErrorHandler) Option
func WithExtractors ¶ added in v0.0.4
func WithExtractors(exs ...extract.RequestValueExtractor) Option
func WithHeaderValues ¶ added in v0.0.4
func WithLogger ¶
func WithMemoryLimit ¶ added in v0.0.4
func WithOptions ¶
func WithPathValues ¶ added in v0.0.4
func WithQueryValues ¶ added in v0.0.4
func WithReadLimit ¶ added in v0.0.4
func WithSessionValues ¶ added in v0.0.14
func WithStatusCode ¶ added in v0.1.5
WithStatusCode applies [NewStatusCodeEncoder] to the handler encoder to override the default http.StatusOK success code.
func WithTemplate ¶ added in v0.0.11
type SlogLogger ¶ added in v0.0.10
func (*SlogLogger) LogRequest ¶ added in v0.0.10
func (s *SlogLogger) LogRequest(r *http.Request, err error)
type UnaryFuncAdaptor ¶
UnaryFuncAdaptor extracts a struct from request and calls a domain function with it expecting a struct response.
func NewUnaryFuncAdaptor ¶
func NewUnaryFuncAdaptor[T any, V *T, O any]( domainCall func(context.Context, V) (O, error), withOptions ...Option, ) (*UnaryFuncAdaptor[T, V, O], error)
NewUnaryFuncAdaptor creates a new adaptor for a function that takes a validatable struct and returns a struct.
func (*UnaryFuncAdaptor[T, V, O]) ServeHTTP ¶
func (a *UnaryFuncAdaptor[T, V, O]) ServeHTTP( w http.ResponseWriter, r *http.Request, )
ServeHTTP satisfies http.Handler interface.
type UnaryStringFuncAdaptor ¶ added in v0.0.2
type UnaryStringFuncAdaptor[O any] struct { // contains filtered or unexported fields }
UnaryStringFuncAdaptor extracts a string value from request and calls a domain function with it expecting a struct response.
func NewUnaryStringFuncAdaptor ¶ added in v0.0.2
func NewUnaryStringFuncAdaptor[O any]( domainCall func(context.Context, string) (O, error), stringExtractor extract.StringValueExtractor, withOptions ...Option, ) (*UnaryStringFuncAdaptor[O], error)
NewUnaryStringFuncAdaptor creates a new adaptor for a function that takes a string and returns a struct.
func (*UnaryStringFuncAdaptor[O]) ServeHTTP ¶ added in v0.0.2
func (a *UnaryStringFuncAdaptor[O]) ServeHTTP( w http.ResponseWriter, r *http.Request, )
ServeHTTP satisfies http.Handler interface.
type Validatable ¶
Validatable constrains a domain request. Validation errors are wrapped as InvalidRequestError by the adapter. context.Context is essential for passing locale information that can be retrieved using LanguageFromContext inside the validation method and other similar uses.
DEPRECATED: will be removed from 1.0 release.
type VoidFuncAdaptor ¶
type VoidFuncAdaptor[T any, V *T] struct { // contains filtered or unexported fields }
VoidStringFuncAdaptor calls a domain function with decoded request without returning no response other than an error.
func NewVoidFuncAdaptor ¶
func NewVoidFuncAdaptor[T any, V *T]( domainCall func(context.Context, V) error, withOptions ...Option, ) (*VoidFuncAdaptor[T, V], error)
NewVoidFuncAdaptor creates a new adaptor for a function that takes a decoded request and returns nothing.
func (*VoidFuncAdaptor[T, V]) ServeHTTP ¶
func (a *VoidFuncAdaptor[T, V]) ServeHTTP( w http.ResponseWriter, r *http.Request, )
ServeHTTP satisfies http.Handler interface.
type VoidStringFuncAdaptor ¶ added in v0.0.2
type VoidStringFuncAdaptor struct {
// contains filtered or unexported fields
}
VoidStringFuncAdaptor extracts a string value from request and calls a domain function with it without expecting no response other than an error value.
func NewVoidStringFuncAdaptor ¶ added in v0.0.2
func NewVoidStringFuncAdaptor( domainCall func(context.Context, string) error, stringExtractor extract.StringValueExtractor, withOptions ...Option, ) (*VoidStringFuncAdaptor, error)
NewVoidStringFuncAdaptor creates a new adaptor for a function that takes a string and returns nothing.
func (*VoidStringFuncAdaptor) ServeHTTP ¶ added in v0.0.2
func (a *VoidStringFuncAdaptor) ServeHTTP( w http.ResponseWriter, r *http.Request, )
ServeHTTP satisfies http.Handler interface.
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
chivalues
module
|
|
examples
|
|
cookie
Package main demonstrates the use of cookie extractor for request decoding.
|
Package main demonstrates the use of cookie extractor for request decoding. |
header
Package main demonstrates the use of header extractor for request decoding.
|
Package main demonstrates the use of header extractor for request decoding. |
htmx
Package main demonstrates the use of template to create an HTMX endpoint.
|
Package main demonstrates the use of template to create an HTMX endpoint. |
htmxform
Package main demonstrates an implementation of a standard feedback form with validation and localization.
|
Package main demonstrates an implementation of a standard feedback form with validation and localization. |
htmxform/feedback
Package feedback is a standard contact form.
|
Package feedback is a standard contact form. |
logging
Package main demonstrates the use of custom logger with an adaptor.
|
Package main demonstrates the use of custom logger with an adaptor. |
query
Package main demonstrates the use of URL query value extractor for request decoding.
|
Package main demonstrates the use of URL query value extractor for request decoding. |
session
Package main demonstrates the simplest implementation of a rotating session context.
|
Package main demonstrates the simplest implementation of a rotating session context. |
urlpath
Package main demonstrates the use of URL path value extractor for request decoding.
|
Package main demonstrates the use of URL path value extractor for request decoding. |
webservice
Package main demonstrates application of generic domain adaptors to satisfy [http.Handler] interface.
|
Package main demonstrates application of generic domain adaptors to satisfy [http.Handler] interface. |
Package extract provides a standard set of most common [http.Request] value extractors which populate fields of a decoded generic request struct.
|
Package extract provides a standard set of most common [http.Request] value extractors which populate fields of a decoded generic request struct. |
chivalues
Module
|
|
middleware
|
|
acceptlanguage
Package acceptlanguage provides [htadaptor.Middleware] that injects [language.Tag] into request context.Context which can be recovered using [htadaptor.LanguageFromContext].
|
Package acceptlanguage provides [htadaptor.Middleware] that injects [language.Tag] into request context.Context which can be recovered using [htadaptor.LanguageFromContext]. |
ctxlogger
Package ctxlogger provides an [htadaptor.Middleware] that injects a given logger into context so that it can be recovered later in the stack for use.
|
Package ctxlogger provides an [htadaptor.Middleware] that injects a given logger into context so that it can be recovered later in the stack for use. |
idledown
Package idledown provides an [htadaptor.Middleware] that shuts the server down due to inactivity.
|
Package idledown provides an [htadaptor.Middleware] that shuts the server down due to inactivity. |
session
Package session presents a lazy context that manages session state with native key rotation support.
|
Package session presents a lazy context that manages session state with native key rotation support. |
session/secrets
Package secrets provides a synchronizable set of keys backed by a key-value store.
|
Package secrets provides a synchronizable set of keys backed by a key-value store. |
session/token
Package token provides [Authority]s that issue and validate access tokens.
|
Package token provides [Authority]s that issue and validate access tokens. |
session/token/gorilla
Package gorilla couples [securecookie.SecureCookie] token encoder with [secrets.Rotation].
|
Package gorilla couples [securecookie.SecureCookie] token encoder with [secrets.Rotation]. |
session/token/jwt
Package jwt provides [token.Authority] with secure JSON Web Tokens issuer defaults.
|
Package jwt provides [token.Authority] with secure JSON Web Tokens issuer defaults. |
tmodulator
Package tmodulator delays HTTP responses to protects wrapped endpoints from timing attacks.
|
Package tmodulator delays HTTP responses to protects wrapped endpoints from timing attacks. |
traceid
Package traceid provides [htadaptor.Middleware] that injects trace identifiers into request context.Context and a matching [slog.Handler] for populating the log records with the identifier.
|
Package traceid provides [htadaptor.Middleware] that injects trace identifiers into request context.Context and a matching [slog.Handler] for populating the log records with the identifier. |
unpanic
Package unpanic provides [htadaptor.Middleware] that gracefully recovers from request panics.
|
Package unpanic provides [htadaptor.Middleware] that gracefully recovers from request panics. |
Package reflectd provides configurable HTTP form body to struct decoder that depends on Gorilla's schema reflection package.
|
Package reflectd provides configurable HTTP form body to struct decoder that depends on Gorilla's schema reflection package. |
schema
Package schema fills a struct with form values.
|
Package schema fills a struct with form values. |
Package service provides a standard library [http.Server] with conventional production defaults and a smooth configuration interface.
|
Package service provides a standard library [http.Server] with conventional production defaults and a smooth configuration interface. |