Documentation ¶
Index ¶
- Constants
- Variables
- func ErrorHandler(resp http.ResponseWriter, req *http.Request, err error)
- func UnmarshalRequestData(data map[string][]string, dst any, structTagKey string, structPrefix string) error
- type ApiError
- func NewApiError(status int, message string, rawErrData any) *ApiError
- func NewBadRequestError(message string, rawErrData any) *ApiError
- func NewForbiddenError(message string, rawErrData any) *ApiError
- func NewInternalServerError(message string, rawErrData any) *ApiError
- func NewNotFoundError(message string, rawErrData any) *ApiError
- func NewTooManyRequestsError(message string, rawErrData any) *ApiError
- func NewUnauthorizedError(message string, rawErrData any) *ApiError
- func ToApiError(err error) *ApiError
- type Event
- func (e *Event) BadRequestError(message string, errData any) *ApiError
- func (e *Event) BindBody(dst any) error
- func (e *Event) Blob(status int, contentType string, b []byte) error
- func (e *Event) Error(status int, message string, errData any) *ApiError
- func (e *Event) FileFS(fsys fs.FS, filename string) error
- func (e *Event) FindUploadedFiles(key string) ([]*filesystem.File, error)
- func (e *Event) Flush() error
- func (e *Event) ForbiddenError(message string, errData any) *ApiError
- func (e *Event) Get(key string) any
- func (e *Event) GetAll() map[string]any
- func (e *Event) HTML(status int, data string) error
- func (e *Event) InternalServerError(message string, errData any) *ApiError
- func (e *Event) IsTLS() bool
- func (e *Event) JSON(status int, data any) error
- func (e *Event) NoContent(status int) error
- func (e *Event) NotFoundError(message string, errData any) *ApiError
- func (e *Event) Redirect(status int, url string) error
- func (e *Event) RemoteIP() string
- func (e *Event) Set(key string, value any)
- func (e *Event) SetAll(m map[string]any)
- func (e *Event) SetCookie(cookie *http.Cookie)
- func (e *Event) Status() int
- func (e *Event) Stream(status int, contentType string, reader io.Reader) error
- func (e *Event) String(status int, data string) error
- func (e *Event) TooManyRequestsError(message string, errData any) *ApiError
- func (e *Event) UnauthorizedError(message string, errData any) *ApiError
- func (e *Event) Written() bool
- func (e *Event) XML(status int, data any) error
- type EventCleanupFunc
- type EventFactoryFunc
- type RWUnwrapper
- type RereadableReadCloser
- type Rereader
- type ResponseWriter
- func (rw *ResponseWriter) Flush()
- func (rw *ResponseWriter) FlushError() error
- func (rw *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
- func (rw *ResponseWriter) Push(target string, opts *http.PushOptions) error
- func (rw *ResponseWriter) ReadFrom(r io.Reader) (n int64, err error)
- func (rw *ResponseWriter) Status() int
- func (rw *ResponseWriter) Unwrap() http.ResponseWriter
- func (rw *ResponseWriter) Write(b []byte) (int, error)
- func (rw *ResponseWriter) WriteHeader(status int)
- func (rw *ResponseWriter) Written() bool
- type Route
- type Router
- type RouterGroup
- func (group *RouterGroup[T]) Any(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) Bind(middlewares ...*hook.Handler[T]) *RouterGroup[T]
- func (group *RouterGroup[T]) BindFunc(middlewareFuncs ...func(e T) error) *RouterGroup[T]
- func (group *RouterGroup[T]) DELETE(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) GET(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) Group(prefix string) *RouterGroup[T]
- func (group *RouterGroup[T]) HEAD(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) HasRoute(method string, path string) bool
- func (group *RouterGroup[T]) OPTIONS(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) PATCH(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) POST(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) PUT(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) Route(method string, path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) SEARCH(path string, action func(e T) error) *Route[T]
- func (group *RouterGroup[T]) Unbind(middlewareIds ...string) *RouterGroup[T]
- type SafeErrorItem
- type SafeErrorParamsResolver
- type SafeErrorResolver
- type StatusTracker
- type WriteTracker
Constants ¶
const DefaultMaxMemory = 32 << 20 // 32mb
const IndexPage = "index.html"
const JSONPayloadKey string = "@jsonPayload"
JSONPayloadKey is the key for the special UnmarshalRequestData case used for reading serialized json payload without normalization.
Variables ¶
var ErrFileNotFound = NewNotFoundError("File not found", nil)
var ErrInvalidRedirectStatusCode = NewInternalServerError("Invalid redirect status code", nil)
var ErrUnsupportedContentType = NewBadRequestError("Unsupported Content-Type", nil)
Functions ¶
func ErrorHandler ¶
func ErrorHandler(resp http.ResponseWriter, req *http.Request, err error)
func UnmarshalRequestData ¶
func UnmarshalRequestData(data map[string][]string, dst any, structTagKey string, structPrefix string) error
UnmarshalRequestData unmarshals url.Values type of data (query, multipart/form-data, etc.) into dst.
dst must be a pointer to a map[string]any or struct.
If dst is a map[string]any, each data value will be inferred and converted to its bool, numeric, or string equivalent value (refer to inferValue() for the exact rules).
If dst is a struct, the following field types are supported:
- bool
- string
- int, int8, int16, int32, int64
- uint, uint8, uint16, uint32, uint64
- float32, float64
- serialized json string if submitted under the special "@jsonPayload" key
- encoding.TextUnmarshaler
- pointer and slice variations of the above primitives (ex. *string, []string, *[]string []*string, etc.)
- named/anonymous struct fields Dot-notation is used to target nested fields, ex. "nestedStructField.title".
- embedded struct fields The embedded struct fields are treated by default as if they were defined in their parent struct. If the embedded struct has a tag matching structTagKey then to set its fields the data keys must be prefixed with that tag similar to the regular nested struct fields.
structTagKey and structPrefix are used only when dst is a struct.
structTagKey represents the tag to use to match a data entry with a struct field (defaults to "form"). If the struct field doesn't have the structTagKey tag, then the exported struct field name will be used as it is.
structPrefix could be provided if all of the data keys are prefixed with a common string and you want the struct field to match only the value without the structPrefix (ex. for "user.name", "user.email" data keys and structPrefix "user", it will match "name" and "email" struct fields).
Note that while the method was inspired by binders from echo, gorrila/schema, ozzo-routing and other similar common routing packages, it is not intended to be a drop-in replacement.
@todo Consider adding support for dot-notation keys, in addition to the prefix, (ex. parent.child.title) to express nested object keys.
Types ¶
type ApiError ¶
type ApiError struct { Data map[string]any `json:"data"` Message string `json:"message"` Status int `json:"status"` // contains filtered or unexported fields }
ApiError defines the struct for a basic api error response.
func NewApiError ¶
NewApiError creates and returns new normalized ApiError instance.
func NewBadRequestError ¶
NewBadRequestError creates and returns 400 ApiError.
func NewForbiddenError ¶
NewForbiddenError creates and returns 403 ApiError.
func NewInternalServerError ¶
NewInternalServerError creates and returns 500 ApiError.
func NewNotFoundError ¶
NewNotFoundError creates and returns 404 ApiError.
func NewTooManyRequestsError ¶
func NewUnauthorizedError ¶
NewUnauthorizedError creates and returns 401 ApiError.
func ToApiError ¶
ToApiError wraps err into ApiError instance (if not already).
type Event ¶
type Event struct { Response http.ResponseWriter Request *http.Request hook.Event // contains filtered or unexported fields }
Event specifies based Route handler event that is usually intended to be embedded as part of a custom event struct.
NB! It is expected that the Response and Request fields are always set.
func (*Event) BadRequestError ¶
func (*Event) BindBody ¶
BindBody unmarshal the request body into the provided dst.
dst must be either a struct pointer or map[string]any.
The rules how the body will be scanned depends on the request Content-Type.
Currently the following Content-Types are supported:
- application/json
- text/xml, application/xml
- multipart/form-data, application/x-www-form-urlencoded
Respectively the following struct tags are supported (again, which one will be used depends on the Content-Type):
- "json" (json body)- uses the builtin Go json package for unmarshaling.
- "xml" (xml body) - uses the builtin Go xml package for unmarshaling.
- "form" (form data) - utilizes the custom router.UnmarshalRequestData method.
NB! When dst is a struct make sure that it doesn't have public fields that shouldn't be bindable and it is advisible such fields to be unexported or have a separate struct just for the binding. For example:
data := struct{ somethingPrivate string Title string `json:"title" form:"title"` Total int `json:"total" form:"total"` } err := e.BindBody(&data)
func (*Event) FileFS ¶
FileFS serves the specified filename from fsys.
It is similar to [echo.FileFS] for consistency with earlier versions.
func (*Event) FindUploadedFiles ¶
func (e *Event) FindUploadedFiles(key string) ([]*filesystem.File, error)
FindUploadedFiles extracts all form files of "key" from a http request and returns a slice with filesystem.File instances (if any).
func (*Event) Flush ¶
Flush flushes buffered data to the current response.
Returns http.ErrNotSupported if e.Response doesn't implement the http.Flusher interface (all router package handlers receives a ResponseWritter that implements it unless explicitly replaced with a custom one).
func (*Event) ForbiddenError ¶
func (*Event) InternalServerError ¶
func (*Event) IsTLS ¶
IsTLS reports whether the connection on which the request was received is TLS.
func (*Event) JSON ¶
JSON writes a JSON response.
It also provides a generic response data fields picker if the "fields" query parameter is set.
func (*Event) Redirect ¶
Redirect writes a redirect response to the specified url. The status code must be in between 300 – 399 range.
func (*Event) RemoteIP ¶
RemoteIP returns the IP address of the client that sent the request.
IPv6 addresses are returned expanded. For example, "2001:db8::1" becomes "2001:0db8:0000:0000:0000:0000:0000:0001".
Note that if you are behind reverse proxy(ies), this method returns the IP of the last connecting proxy.
func (*Event) SetCookie ¶
SetCookie is an alias for http.SetCookie.
SetCookie adds a Set-Cookie header to the current response's headers. The provided cookie must have a valid Name. Invalid cookies may be silently dropped.
func (*Event) Status ¶
Status reports the status code of the current response.
This method always returns 0 if e.Response doesn't implement the StatusTracker interface (all router package handlers receives a ResponseWritter that implements it unless explicitly replaced with a custom one).
func (*Event) TooManyRequestsError ¶
func (*Event) UnauthorizedError ¶
func (*Event) Written ¶
Written reports whether the current response has already been written.
This method always returns false if e.ResponseWritter doesn't implement the WriteTracker interface (all router package handlers receives a ResponseWritter that implements it unless explicitly replaced with a custom one).
type EventCleanupFunc ¶
type EventCleanupFunc func()
type EventFactoryFunc ¶
type EventFactoryFunc[T hook.Resolver] func(w http.ResponseWriter, r *http.Request) (T, EventCleanupFunc)
EventFactoryFunc defines the function responsible for creating a Route specific event based on the provided request handler ServeHTTP data.
Optionally return a clean up function that will be invoked right after the route execution.
type RWUnwrapper ¶
type RWUnwrapper interface {
Unwrap() http.ResponseWriter
}
RWUnwrapper specifies that an http.ResponseWriter could be "unwrapped" (usually used with http.ResponseController).
type RereadableReadCloser ¶
type RereadableReadCloser struct { io.ReadCloser // contains filtered or unexported fields }
RereadableReadCloser defines a wrapper around a io.ReadCloser reader allowing to read the original reader multiple times.
func (*RereadableReadCloser) Read ¶
func (r *RereadableReadCloser) Read(b []byte) (int, error)
Read implements the standard io.Reader interface.
It reads up to len(b) bytes into b and at at the same time writes the read data into an internal bytes buffer.
On EOF the r is "rewinded" to allow reading from r multiple times.
func (*RereadableReadCloser) Reread ¶
func (r *RereadableReadCloser) Reread()
Reread satisfies the Rereader interface and resets the r internal state to allow rereads.
note: not named Reset to avoid conflicts with other reader interfaces.
type Rereader ¶
type Rereader interface {
Reread()
}
Rereader defines an interface for rewindable readers.
type ResponseWriter ¶
type ResponseWriter struct { http.ResponseWriter // contains filtered or unexported fields }
ResponseWriter wraps a http.ResponseWriter to track its write state.
func (*ResponseWriter) Flush ¶
func (rw *ResponseWriter) Flush()
Flush implements http.Flusher and allows an HTTP handler to flush buffered data to the client. This method is no-op if the wrapped writer doesn't support it.
func (*ResponseWriter) FlushError ¶
func (rw *ResponseWriter) FlushError() error
FlushError is similar to [Flush] but returns http.ErrNotSupported if the wrapped writer doesn't support it.
func (*ResponseWriter) Hijack ¶
func (rw *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
Hijack implements http.Hijacker and allows an HTTP handler to take over the current connection.
func (*ResponseWriter) Push ¶
func (rw *ResponseWriter) Push(target string, opts *http.PushOptions) error
Pusher implements http.Pusher to indicate HTTP/2 server push support.
func (*ResponseWriter) ReadFrom ¶
func (rw *ResponseWriter) ReadFrom(r io.Reader) (n int64, err error)
ReaderFrom implements io.ReaderFrom by checking if the underlying writer supports it. Otherwise calls io.Copy.
func (*ResponseWriter) Status ¶
func (rw *ResponseWriter) Status() int
Written implements StatusTracker and returns the written status code of the current response.
func (*ResponseWriter) Unwrap ¶
func (rw *ResponseWriter) Unwrap() http.ResponseWriter
Unwrap returns the underlying ResponseWritter instance (usually used by http.ResponseController).
func (*ResponseWriter) WriteHeader ¶
func (rw *ResponseWriter) WriteHeader(status int)
func (*ResponseWriter) Written ¶
func (rw *ResponseWriter) Written() bool
Written implements WriteTracker and returns whether the current response body has been already written.
type Route ¶
type Route[T hook.Resolver] struct { Action func(e T) error Method string Path string Middlewares []*hook.Handler[T] // contains filtered or unexported fields }
func (*Route[T]) BindFunc ¶
BindFunc registers one or multiple middleware functions to the current route.
The registered middleware functions are "anonymous" and with default priority, aka. executes in the order they were registered.
If you need to specify a named middleware (ex. so that it can be removed) or middleware with custom exec prirority, use the Route.Bind method.
func (*Route[T]) Unbind ¶
Unbind removes one or more middlewares with the specified id(s) from the current route.
It also adds the removed middleware ids to an exclude list so that they could be skipped from the execution chain in case the middleware is registered in a parent group.
Anonymous middlewares are considered non-removable, aka. this method does nothing if the middleware id is an empty string.
type Router ¶
type Router[T hook.Resolver] struct { // @todo consider renaming the type to just Group and replace the embed type // with an alias after Go 1.24 adds support for generic type aliases *RouterGroup[T] // contains filtered or unexported fields }
Router defines a thin wrapper around the standard Go http.ServeMux by adding support for routing sub-groups, middlewares and other common utils.
Example:
r := NewRouter[*MyEvent](eventFactory) // middlewares r.BindFunc(m1, m2) // routes r.GET("/test", handler1) // sub-routers/groups api := r.Group("/api") api.GET("/admins", handler2) // generate a http.ServeMux instance based on the router configurations mux, _ := r.BuildMux() http.ListenAndServe("localhost:8090", mux)
type RouterGroup ¶
type RouterGroup[T hook.Resolver] struct { Prefix string Middlewares []*hook.Handler[T] // contains filtered or unexported fields }
RouterGroup represents a collection of routes and other sub groups that share common pattern prefix and middlewares.
func (*RouterGroup[T]) Any ¶
func (group *RouterGroup[T]) Any(path string, action func(e T) error) *Route[T]
Any is a shorthand for [RouterGroup.AddRoute] with "" as route method (aka. matches any method).
func (*RouterGroup[T]) Bind ¶
func (group *RouterGroup[T]) Bind(middlewares ...*hook.Handler[T]) *RouterGroup[T]
Bind registers one or multiple middleware handlers to the current group.
func (*RouterGroup[T]) BindFunc ¶
func (group *RouterGroup[T]) BindFunc(middlewareFuncs ...func(e T) error) *RouterGroup[T]
BindFunc registers one or multiple middleware functions to the current group.
The registered middleware functions are "anonymous" and with default priority, aka. executes in the order they were registered.
If you need to specify a named middleware (ex. so that it can be removed) or middleware with custom exec prirority, use RouterGroup.Bind method.
func (*RouterGroup[T]) DELETE ¶
func (group *RouterGroup[T]) DELETE(path string, action func(e T) error) *Route[T]
DELETE is a shorthand for [RouterGroup.AddRoute] with DELETE as route method.
func (*RouterGroup[T]) GET ¶
func (group *RouterGroup[T]) GET(path string, action func(e T) error) *Route[T]
GET is a shorthand for [RouterGroup.AddRoute] with GET as route method.
func (*RouterGroup[T]) Group ¶
func (group *RouterGroup[T]) Group(prefix string) *RouterGroup[T]
Group creates and register a new child Group into the current one with the specified prefix.
The prefix follows the standard Go net/http ServeMux pattern format ("[HOST]/[PATH]") and will be concatenated recursively into the final route path, meaning that only the root level group could have HOST as part of the prefix.
Returns the newly created group to allow chaining and registering sub-routes and group specific middlewares.
func (*RouterGroup[T]) HEAD ¶
func (group *RouterGroup[T]) HEAD(path string, action func(e T) error) *Route[T]
HEAD is a shorthand for [RouterGroup.AddRoute] with HEAD as route method.
func (*RouterGroup[T]) HasRoute ¶
func (group *RouterGroup[T]) HasRoute(method string, path string) bool
HasRoute checks whether the specified route pattern (method + path) is registered in the current group or its children.
This could be useful to conditionally register and checks for routes in order prevent panic on duplicated routes.
Note that routes with anonymous and named wildcard placeholder are treated as equal, aka. "GET /abc/" is considered the same as "GET /abc/{something...}".
func (*RouterGroup[T]) OPTIONS ¶
func (group *RouterGroup[T]) OPTIONS(path string, action func(e T) error) *Route[T]
OPTIONS is a shorthand for [RouterGroup.AddRoute] with OPTIONS as route method.
func (*RouterGroup[T]) PATCH ¶
func (group *RouterGroup[T]) PATCH(path string, action func(e T) error) *Route[T]
PATCH is a shorthand for [RouterGroup.AddRoute] with PATCH as route method.
func (*RouterGroup[T]) POST ¶
func (group *RouterGroup[T]) POST(path string, action func(e T) error) *Route[T]
POST is a shorthand for [RouterGroup.AddRoute] with POST as route method.
func (*RouterGroup[T]) PUT ¶
func (group *RouterGroup[T]) PUT(path string, action func(e T) error) *Route[T]
PUT is a shorthand for [RouterGroup.AddRoute] with PUT as route method.
func (*RouterGroup[T]) Route ¶
func (group *RouterGroup[T]) Route(method string, path string, action func(e T) error) *Route[T]
Route registers a single route into the current group.
Note that the final route path will be the concatenation of all parent groups prefixes + the route path. The path follows the standard Go net/http ServeMux format ("[HOST]/[PATH]"), meaning that only a top level group route could have HOST as part of the prefix.
Returns the newly created route to allow attaching route-only middlewares.
func (*RouterGroup[T]) SEARCH ¶
func (group *RouterGroup[T]) SEARCH(path string, action func(e T) error) *Route[T]
SEARCH is a shorthand for [RouterGroup.AddRoute] with SEARCH as route method.
func (*RouterGroup[T]) Unbind ¶
func (group *RouterGroup[T]) Unbind(middlewareIds ...string) *RouterGroup[T]
Unbind removes one or more middlewares with the specified id(s) from the current group and its children (if any).
Anonymous middlewares are not removable, aka. this method does nothing if the middleware id is an empty string.
type SafeErrorItem ¶
type SafeErrorItem interface { // Code represents a fixed unique identifier of the error (usually used as translation key). Code() string // Error is the default English human readable error message that will be returned. Error() string }
SafeErrorItem defines a common error interface for a printable public safe error.
type SafeErrorParamsResolver ¶
type SafeErrorParamsResolver interface { // Params defines a map with dynamic parameters to return as part of the public safe error view. Params() map[string]any }
SafeErrorParamsResolver defines an optional interface for specifying dynamic error parameters.
type SafeErrorResolver ¶
type SafeErrorResolver interface { // Resolve allows modifying and returning a new public safe error data map. Resolve(errData map[string]any) any }
SafeErrorResolver defines an error interface for resolving the public safe error fields.
type StatusTracker ¶
type StatusTracker interface { // Status reports the written response status code. Status() int }
type WriteTracker ¶
type WriteTracker interface { // Written reports whether a write operation has occurred. Written() bool }