Documentation
¶
Index ¶
- Constants
- Variables
- func Func(f func(Context) error) func() Handler
- func ToClientJSON(err error) []byte
- func WithAlarmer(ala Alarmer) func(*Option)
- func WithClientErrorFields(fields ...string) func(*Option)
- func WithDefaultLogOption(lo LogOption) func(*Option)
- func WithDefaultTimeout(d time.Duration) func(*Option)
- func WithJsonMasker(jm JsonMasker) func(*Option)
- func WithLogger(log *zerolog.Logger) func(*WsOption)
- func WithMetricReporter(mr MetricReporter) func(*Option)
- func WithRequestID() func(*Option)
- func WithServerConfig(f func(*fasthttp.Server)) func(*Option)
- func WithStaticLoggingLevel() func(*Option)
- func WithTokenDecoder(td TokenDecoder) func(*WsOption)
- func WithUrlPrefix(s string) func(*Option)
- func WithVerboseError(b bool) func(*Option)
- func WithWebsocketSupport(wv *WebsocketVatel) func(*Option)
- func WithWsAuthFunc(fn func(ctx WebsocketContext, path string, requiredPerms []string) error) func(*WsOption)
- func WithWsAuthTimeout(d time.Duration) func(*WsOption)
- func WithWsWriteTimeout(d time.Duration) func(*WsOption)
- type AccessTokenUpdateHandler
- type Alarmer
- type AuthMessageDatadeprecated
- type AuthPayload
- type Body
- type Context
- type Endpoint
- func DELETE(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func GET(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func HEAD(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func NewEndpoint(method, path string, perms []string, c func() Handler) *Endpoint
- func OPTIONS(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func PATCH(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func POST(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func PUT(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
- func WS(path string, ctrl func() WebsocketHandler, opts ...EndpointOption) Endpoint
- type EndpointMeta
- type EndpointOption
- func Perms(p ...string) EndpointOption
- func WithCompress() EndpointOption
- func WithContentType(ct string) EndpointOption
- func WithLogOption(lo LogOption) EndpointOption
- func WithManualStatusCode() EndpointOption
- func WithOptionalBody() EndpointOption
- func WithSuccessStatusCode(code int) EndpointOption
- func WithTimeout(d time.Duration) EndpointOption
- type Endpointer
- type Endpoints
- type Expirer
- type Handler
- type Inputer
- type JsonMasker
- type LogOption
- type MetricReporter
- type MiddlewarePos
- type Option
- type OptionalOutput
- type Output
- type Paramer
- type PathParams
- type Querier
- type QueryParams
- type Resulter
- type RevokeTokenChecker
- type TokenDecoder
- type Tokener
- type Upload
- type Validator
- type Vatel
- func (v *Vatel) Add(e ...Endpointer)
- func (v *Vatel) AddMiddleware(pos MiddlewarePos, f ...func(Context, EndpointMeta) error)
- func (v *Vatel) BuildHandlers(mux *router.Router, l *zerolog.Logger) error
- func (v *Vatel) Endpoints() []Endpoint
- func (v *Vatel) ListenAndServe(addr string, mux *router.Router, l *zerolog.Logger) error
- func (v *Vatel) MustBuildHandlers(mux *router.Router, l *zerolog.Logger)
- func (v *Vatel) Shutdown(ctx context.Context) error
- type VatelContext
- func (c *VatelContext) BodyWriter() io.Writer
- func (c *VatelContext) Context() context.Context
- func (c *VatelContext) FormFile(key string) (*multipart.FileHeader, error)
- func (c *VatelContext) FormValue(key string) []byte
- func (c *VatelContext) Get(key string) any
- func (c *VatelContext) Header(name string) []byte
- func (c *VatelContext) PathValue(name string) string
- func (c *VatelContext) QueryValue(name string) []byte
- func (c *VatelContext) RemoteAddr() string
- func (c *VatelContext) RequestBody() []byte
- func (c *VatelContext) RequestTime() time.Time
- func (c *VatelContext) SaveMultipartFile(fh *multipart.FileHeader, path string) error
- func (c *VatelContext) Set(key string, val any) Context
- func (c *VatelContext) SetContentType(ct []byte) Context
- func (c *VatelContext) SetHeader(name, val []byte) Context
- func (c *VatelContext) SetStatusCode(code int) Context
- func (c *VatelContext) SetTokenPayload(tp any)
- func (c *VatelContext) SetValue(key, val any) Context
- func (c *VatelContext) SetVerboseError(v bool)
- func (c *VatelContext) TokenPayload() any
- func (c *VatelContext) VerboseError() bool
- func (c *VatelContext) VisitUserValues(f func(key []byte, val any))
- type WebsocketContext
- type WebsocketHandler
- type WebsocketRequest
- type WebsocketVatel
- func (wv *WebsocketVatel) CloseConn(cid uint64, status websocket.StatusCode, reason string) bool
- func (wv *WebsocketVatel) Endpoints() []Endpoint
- func (wv *WebsocketVatel) OnClose(f func(*websocket.Conn, error))
- func (wv *WebsocketVatel) OnOpen(f func(*websocket.Conn))
- func (wv *WebsocketVatel) OnTokenUpdate(f func(cid uint64, prev, next AuthPayload) error)
- func (wv *WebsocketVatel) RegisterEndpoint(v *Vatel, e *Endpoint, l *zerolog.Logger) error
- func (wv *WebsocketVatel) Shutdown()
- func (wv *WebsocketVatel) UpdateAccessToken(cid uint64, token string) error
- func (wv *WebsocketVatel) Upgrade(ctx *fasthttp.RequestCtx)
- func (wv *WebsocketVatel) UpgradeWithID(ctx *fasthttp.RequestCtx, id uint64)
- func (wv *WebsocketVatel) VisitConns(f func(cid uint64, tp AuthPayload) bool)
- type WsCloseError
- type WsContext
- func (c *WsContext) BodyWriter() io.Writer
- func (c *WsContext) Created() time.Time
- func (c *WsContext) Get(key string) any
- func (c *WsContext) ID() uint64
- func (c *WsContext) Set(key string, val any) WebsocketContext
- func (c *WsContext) SetTokenPayload(tp any)
- func (c *WsContext) TokenPayload() any
- func (c *WsContext) VisitValues(f func(key []byte, v any))
- type WsOption
Constants ¶
const ( LogUnknown LogOption = 0 LogFull = LogEnter | LogExit | LogReqBody | LogReqInput | LogRespBody LogFullOnExit = LogExit | LogReqBody | LogReqInput | LogRespBody LogConfidential = LogExit )
const ( // WsStatusAuthRequired (4001): the connection has no valid token (its // token expired, or none came within WithWsAuthTimeout). The client gets // a fresh token and reconnects. WsStatusAuthRequired websocket.StatusCode = 4001 // WsStatusForbidden (4003): the connection may not continue (another // user, access revoked). The client does not reconnect. WsStatusForbidden websocket.StatusCode = 4003 // WsStatusContextChanged (4009): the token is of another security // context (e.g. organization) than the connection. The client gets a // token of the context it needs and opens a new connection with it. WsStatusContextChanged websocket.StatusCode = 4009 )
Close statuses of the WebSocket connections closed by the server for authentication reasons; WsCloseError carries one. Applications may define their own in the 4000–4999 range. A client reacts to them as follows.
const DefaultIdleTimeout = 90 * time.Second
DefaultIdleTimeout is the IdleTimeout of the server started by ListenAndServe: a keep-alive connection without a new request is closed after it. Change it with WithServerConfig.
const DefaultWsWriteTimeout = 10 * time.Second
DefaultWsWriteTimeout is the default write timeout of a WebSocket frame (see WithWsWriteTimeout).
Variables ¶
var ( // ErrAuthorizationHeaderMissed was returned for a request without the // Authorization header. // // Deprecated: neither vatel nor vatel/auth returns it; a request without // a token gets ErrUnauthorized (VTL-0004). Kept for compatibility. ErrAuthorizationHeaderMissed = errors.Template("header Authorization missed").Code("VTL-0001").StatusCode(401).Severity(errors.Medium) ErrAccessTokenRevoked = errors.Template("access token revoked").Code("VTL-0002").StatusCode(401).Severity(errors.Medium) ErrForbidden = errors.Template("forbidden").Code("VTL-0003").StatusCode(403).Severity(errors.Medium) ErrInternalError = errors.Template("internal error").Code("VTL-0005").StatusCode(500).Severity(errors.Critical) ErrValidationFailed = errors.Template("validation failed").Code("VTL-0006").StatusCode(400).Severity(errors.Tiny) ErrInvalidRequestBody = errors.Template("invalid request body").Code("VTL-0007").StatusCode(400).Severity(errors.Tiny) // ErrUnknownPath is sent to a WebSocket client for a message whose path // names no WS endpoint. ErrUnknownPath = errors.Template("unknown path").Code("VTL-0008").StatusCode(404).Severity(errors.Tiny) // ErrWsContextChanged is sent to a WebSocket client for a message whose // WsCloseError with WsStatusContextChanged has no error of its own. ErrWsContextChanged = errors.Template("security context changed").Code("VTL-0009").StatusCode(409).Severity(errors.Medium) // ErrWsTokenConflict is returned by WebsocketVatel.UpdateAccessToken when // another payload was stored for the connection, or it closed, while the // OnTokenUpdate callback ran. ErrWsTokenConflict = errors.Template("concurrent token update").Code("VTL-0010").StatusCode(409).Severity(errors.Medium) )
Sentinel errors.
var ErrServerClosed = stderrors.New("vatel: server closed")
ErrServerClosed is returned by ListenAndServe called after Shutdown.
Functions ¶
func Func ¶ added in v1.25.0
Func wraps a plain function as a per-request Handler factory, suitable for simple stateless endpoints that do not need a full controller struct. It may be used on a path with {params}: read them with ctx.PathValue (they are not logged).
vatel.DELETE("/orders/{id}", vatel.Func(func(ctx vatel.Context) error {
id := ctx.PathValue("id")
return repo.Delete(id)
}), vatel.Perms("orders:write"))
func ToClientJSON ¶ added in v0.14.0
ToClientJSON serialises an error for transmission to the WebSocket client.
func WithAlarmer ¶ added in v0.10.0
WithAlarmer sets the receiver of errors with status >= 500.
func WithClientErrorFields ¶ added in v1.25.0
WithClientErrorFields lists error fields (set via errors.Set) that are safe to show to the client. They are written at the root of the error JSON even when verbose errors are off; all other fields stay in the server log only.
vatel.WithClientErrorFields("reason", "suggestedName")
func WithDefaultLogOption ¶ added in v0.7.0
WithDefaultLogOption sets the log verbosity of endpoints without their own WithLogOption. Without it only errors are logged.
func WithDefaultTimeout ¶ added in v1.25.0
WithDefaultTimeout sets the request context timeout of endpoints without their own WithTimeout. See WithTimeout.
func WithJsonMasker ¶ added in v0.10.0
func WithJsonMasker(jm JsonMasker) func(*Option)
WithJsonMasker sets the masker applied to logged request and response bodies, inputs and results of types with `mask` struct tags. Without it a default jsonmask.New() is used, so tagged fields are always masked in the log ("***" for a function name not registered with AddFunc).
func WithLogger ¶ added in v0.13.2
WithLogger sets the logger of WS endpoints; their lines get "layer":"ws". Without it WS endpoints log to the logger passed to BuildHandlers.
func WithMetricReporter ¶ added in v0.11.0
func WithMetricReporter(mr MetricReporter) func(*Option)
WithMetricReporter sets the reporter of per-request metrics: method, path template, status, duration and response size (0 for HEAD).
func WithRequestID ¶ added in v0.8.0
func WithRequestID() func(*Option)
WithRequestID logs the fasthttp request ID as "reqId" (connectionId for WS messages); it is also stored in the request context under "reqId".
func WithServerConfig ¶ added in v1.25.0
WithServerConfig sets a function that configures the fasthttp.Server started by ListenAndServe, e.g. its ReadTimeout or MaxRequestBodySize. It is called after vatel has set Handler, IdleTimeout (DefaultIdleTimeout) and CloseOnShutdown (true). Do not replace Handler. Hooks such as ConnState are free for the application: Shutdown does not use them.
func WithStaticLoggingLevel ¶ added in v0.7.0
func WithStaticLoggingLevel() func(*Option)
WithStaticLoggingLevel makes vatel ignore the X-Set-Log-Level request header.
func WithTokenDecoder ¶ added in v0.13.5
func WithTokenDecoder(td TokenDecoder) func(*WsOption)
WithTokenDecoder sets the decoder of tokens sent to the built-in updateAccessToken endpoint. Without it the endpoint stores nothing.
func WithUrlPrefix ¶ added in v0.7.0
WithUrlPrefix sets a prefix joined to the path of every HTTP endpoint, e.g. "/api/v1".
func WithVerboseError ¶ added in v0.7.2
WithVerboseError enables verbose error responses (stack traces, wrapped errors).
func WithWebsocketSupport ¶ added in v0.13.5
func WithWebsocketSupport(wv *WebsocketVatel) func(*Option)
WithWebsocketSupport enables WS endpoints, served by wv. The application routes an HTTP path to wv.Upgrade.
func WithWsAuthFunc ¶ added in v1.25.0
func WithWsAuthFunc(fn func(ctx WebsocketContext, path string, requiredPerms []string) error) func(*WsOption)
WithWsAuthFunc sets the per-message authorization function. It is called for every incoming message before the handler runs. ctx carries the token payload (set via UpdateAccessToken or a prior message), path is the endpoint path, and requiredPerms is the slice declared on the endpoint (may be empty). Return a non-nil error to reject the message.
func WithWsAuthTimeout ¶ added in v1.25.0
WithWsAuthTimeout closes a connection that has no token payload d after it opened (no successful UpdateAccessToken, nor an unexpired payload stored for its ID before it opened) with WsStatusAuthRequired. A rejected token does not count. The default 0 means no limit. It has no effect without WithTokenDecoder.
func WithWsWriteTimeout ¶ added in v1.25.0
WithWsWriteTimeout sets how long writing one frame to a WebSocket client may take; the default is DefaultWsWriteTimeout, 0 means no limit. A write of a client that does not read blocks once the outbound queue of its connection (128 frames) is full; when the timeout expires the write fails and the connection ends, so the handler writing to it does not block forever.
Types ¶
type AccessTokenUpdateHandler ¶ added in v0.13.5
type AccessTokenUpdateHandler struct {
// contains filtered or unexported fields
}
func (*AccessTokenUpdateHandler) Handle ¶ added in v0.13.5
func (c *AccessTokenUpdateHandler) Handle(ctx WebsocketContext) error
Handle implements github.com/axkit/vatel Handler interface. The handler has no logic because if access token is invalid, middleware would not pass it to the handler.
func (*AccessTokenUpdateHandler) Input ¶ added in v0.13.5
func (c *AccessTokenUpdateHandler) Input() any
func (*AccessTokenUpdateHandler) Result ¶ added in v0.13.5
func (c *AccessTokenUpdateHandler) Result() any
type Alarmer ¶ added in v0.10.0
type Alarmer interface {
Alarm(err error)
}
Alarmer receives critical errors for alerting.
type AuthMessageData
deprecated
added in
v0.13.0
type AuthMessageData struct {
AccessToken string `json:"accessToken"`
}
AuthMessageData is the payload for token-based authentication messages.
Deprecated: vatel does not use it; the updateAccessToken endpoint reads {"mid": ..., "at": ...}. Kept for compatibility.
type AuthPayload ¶ added in v1.25.0
AuthPayload is the application part of a decoded token, returned by Tokener.ApplicationPayload. The vatel core does not call its methods; they are used by vatel/auth: Perms for the permission check of RequirePerms and Debug to turn on verbose error responses for the request.
Handlers retrieve the payload via ctx.TokenPayload() and type-assert it to their concrete type (e.g. ctx.TokenPayload().(MyJWTClaims)).
type Body ¶ added in v1.25.0
type Body[T any] struct { In T }
Body is an embeddable generic type that implements Inputer. Embed it and access input data via the In field. For POST/PUT/PATCH the JSON request body is decoded into In; for GET/DELETE/HEAD/OPTIONS the URL query string is decoded into the fields of In tagged `param:"name"` (prefer QueryParams for query input). The name Body avoids the naming conflict that would arise from an embedded type named Input having a promoted method also named Input().
type CreateOrder struct {
api *API
vatel.Body[struct {
Product string `json:"product"`
Quantity int `json:"quantity"`
}]
}
// c.In.Product, c.In.Quantity
If T implements Validator, Validate() is called after decoding.
type Context ¶
type Context interface {
// Response writing
BodyWriter() io.Writer
SetContentType([]byte) Context
SetStatusCode(code int) Context
SetHeader(name, val []byte) Context
// Request reading
Header(name string) []byte
RemoteAddr() string
RequestTime() time.Time
RequestBody() []byte
PathValue(name string) string
QueryValue(name string) []byte
// File handling
FormFile(key string) (*multipart.FileHeader, error)
FormValue(key string) []byte
SaveMultipartFile(fh *multipart.FileHeader, path string) error
// Auth — the payload set by auth middleware, nil if unauthenticated.
// Handlers cast it to their concrete type: ctx.TokenPayload().(*MyClaims).
TokenPayload() any
SetTokenPayload(tp any)
// VerboseError reports whether the auth middleware requested verbose error
// responses for this request (e.g. for debug/admin tokens).
VerboseError() bool
SetVerboseError(bool)
// Per-request key-value store (for middleware ↔ handler communication).
// Values stored with Set are written to the request log.
Set(key string, val any) Context
Get(key string) any
// SetValue stores val under an arbitrary comparable key, e.g. an unexported
// typed key of the application, so that Context().Value(key) returns it.
// A string key is the same as Set; a value under a non-string key is not
// returned by Get and is not written to the request log.
SetValue(key, val any) Context
VisitUserValues(func(key []byte, val any))
// Context returns the request context. It is cancelled by Vatel.Shutdown
// and, when the endpoint has a timeout (WithTimeout, WithDefaultTimeout),
// when the timeout expires or the request ends. fasthttp cannot detect a
// client disconnect, so it is not cancelled when the client goes away.
//
// Its Value(key) returns the request values (Set, SetValue, path params,
// "reqId"), looked up at call time, and then the values of the parent
// context. After the request has ended it returns the parent values only.
Context() context.Context
}
Context is the per-request interface passed to every Handler, middleware, and response hook. It wraps the underlying HTTP request and response without exposing the fasthttp implementation.
func NewContext ¶
func NewContext(fctx *fasthttp.RequestCtx) Context
NewContext creates a Context wrapping the given fasthttp request context. Its Context() is context.Background().
type Endpoint ¶
type Endpoint struct {
// EndpointMeta is the static definition of the endpoint — method, path,
// required permissions, and log verbosity. It is passed as-is to every
// middleware so middleware can make decisions based on endpoint configuration
// without accessing internal state.
EndpointMeta
// Compress enables gzip compression of the response. See WithCompress.
Compress bool
// ManualStatusCode leaves the success status to the handler
// (ctx.SetStatusCode). See WithManualStatusCode.
ManualStatusCode bool
// SuccessStatusCode is the status of a successful response; zero keeps
// 200. See WithSuccessStatusCode.
SuccessStatusCode int
// ResponseContentType is the Content-Type of a successful response; empty
// means "application/json; charset=utf-8". See WithContentType.
ResponseContentType string
// OptionalBody allows an empty request body on POST/PUT/PATCH.
// See WithOptionalBody.
OptionalBody bool
// Timeout of the request context; zero takes the Vatel default
// (WithDefaultTimeout), if any. See WithTimeout.
Timeout time.Duration
// Controller factories — exactly one must be set.
Controller func() Handler
WebsocketController func() WebsocketHandler
// contains filtered or unexported fields
}
Endpoint describes a single REST or WebSocket endpoint and its configuration.
func DELETE ¶ added in v1.25.0
func DELETE(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
DELETE creates a DELETE endpoint with the given path, controller factory, and options.
func GET ¶ added in v1.25.0
func GET(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
GET creates a GET endpoint with the given path, controller factory, and options.
func HEAD ¶ added in v1.25.0
func HEAD(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
HEAD creates a HEAD endpoint with the given path, controller factory, and options. Input is decoded from the URL query string, like GET. The fasthttp server drops the response body of a HEAD request, so only the status code and headers reach the client.
func NewEndpoint ¶ added in v0.3.0
NewEndpoint builds an Endpoint. Prefer the GET/POST/PUT/PATCH/DELETE builder functions from options.go for a more readable call site.
func OPTIONS ¶ added in v1.25.0
func OPTIONS(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
OPTIONS creates an OPTIONS endpoint with the given path, controller factory, and options. Input is decoded from the URL query string, like GET. An explicit OPTIONS endpoint takes precedence over router.GlobalOPTIONS for its path. Do not put RequireAuth/RequirePerms on an endpoint that serves a CORS preflight: browsers send the preflight without the Authorization header.
func PATCH ¶ added in v1.25.0
func PATCH(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
PATCH creates a PATCH endpoint with the given path, controller factory, and options.
func POST ¶ added in v1.25.0
func POST(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
POST creates a POST endpoint with the given path, controller factory, and options.
func PUT ¶ added in v1.25.0
func PUT(path string, ctrl func() Handler, opts ...EndpointOption) Endpoint
PUT creates a PUT endpoint with the given path, controller factory, and options.
func WS ¶ added in v1.25.0
func WS(path string, ctrl func() WebsocketHandler, opts ...EndpointOption) Endpoint
WS creates a WebSocket endpoint with the given path, controller factory, and options.
func (*Endpoint) AddMiddleware ¶ added in v1.25.0
func (e *Endpoint) AddMiddleware(pos MiddlewarePos, f ...func(Context, EndpointMeta) error)
AddMiddleware adds per-endpoint middleware at the given pipeline position. Called from EndpointOption functions (e.g. auth.RequirePerms).
type EndpointMeta ¶ added in v1.25.0
EndpointMeta carries read-only metadata about the endpoint being served. It is passed to every middleware function so middleware can make decisions based on the endpoint's static configuration (route, permissions, log level) without accessing internal endpoint state.
type EndpointOption ¶ added in v1.25.0
type EndpointOption func(*Endpoint)
EndpointOption is a functional option for configuring an Endpoint.
func Perms ¶ added in v1.25.0
func Perms(p ...string) EndpointOption
Perms appends permission names to the endpoint's metadata (EndpointMeta.Perms).
On HTTP endpoints Perms is metadata only: vatel does not check it. The check is done by middleware, e.g. auth.RequirePerms, which also appends its permission names here. On WS endpoints non-empty Perms require WithWsAuthFunc, which receives them; without it BuildHandlers fails.
func WithCompress ¶ added in v1.25.0
func WithCompress() EndpointOption
WithCompress enables gzip compression for the endpoint response.
func WithContentType ¶ added in v1.25.0
func WithContentType(ct string) EndpointOption
WithContentType sets a custom response Content-Type header. Default is "application/json; charset=utf-8".
func WithLogOption ¶ added in v1.25.0
func WithLogOption(lo LogOption) EndpointOption
WithLogOption sets the log verbosity bitmask for the endpoint.
func WithManualStatusCode ¶ added in v1.25.0
func WithManualStatusCode() EndpointOption
WithManualStatusCode disables automatic status code setting, allowing the handler to control it explicitly via ctx.SetStatusCode().
func WithOptionalBody ¶ added in v1.25.0
func WithOptionalBody() EndpointOption
WithOptionalBody allows an empty request body on a POST/PUT/PATCH endpoint. When the body is empty or contains only whitespace, decoding is skipped: the input stays zero and Validator.Validate() is not called. A non-empty body is decoded and validated as usual; invalid JSON is still rejected with 400. Using it on a method without a request body (GET, DELETE, HEAD, OPTIONS) is a compile error returned from BuildHandlers.
func WithSuccessStatusCode ¶ added in v1.25.0
func WithSuccessStatusCode(code int) EndpointOption
WithSuccessStatusCode sets a custom HTTP status code for successful responses. Default is 200.
func WithTimeout ¶ added in v1.25.0
func WithTimeout(d time.Duration) EndpointOption
WithTimeout sets the timeout of the request context returned by Context.Context(); it overrides WithDefaultTimeout. The timeout does not interrupt the handler by itself: only code that listens to the context (database queries, HTTP calls, select on Done()) stops. A handler returning context.DeadlineExceeded gets 504, context.Canceled gets 503.
type Endpointer ¶
type Endpointer interface {
Endpoints() []Endpoint
}
Endpointer is implemented by any type that exposes a set of endpoints.
type Endpoints ¶ added in v1.25.0
type Endpoints []Endpoint
Endpoints is a slice of Endpoint that implements the Endpointer interface. Use it to group related endpoints in one place without defining a named type.
func (a *OrdersAPI) Endpoints() []vatel.Endpoint {
return vatel.Endpoints{
vatel.GET("/orders", func() Handler { return &ListOrders{api: a} }, vatel.Perms("orders:read")),
vatel.POST("/orders", func() Handler { return &CreateOrder{api: a} }, vatel.Perms("orders:write")),
vatel.GET("/orders/{id}", func() Handler { return &GetOrder{api: a} }, vatel.Perms("orders:read")),
}
}
type Expirer ¶ added in v1.25.0
Expirer is implemented by a token payload (AuthPayload) that expires. The WebSocket connection whose payload has a non-zero ExpiresAt is closed with WsStatusAuthRequired at that time, unless the client sends a new token (updateAccessToken) before: once the payload has expired, the connection is not served and takes no new token, even before its expiry timer fires (a message or a token update on it closes it with WsStatusAuthRequired at once). An already expired new payload is rejected by UpdateAccessToken with ErrUnauthorized, and a payload stored for an ID before its connection opened (UpgradeWithID) that has expired by then is dropped: the connection opens without a payload. An expired payload never authorizes a message.
type Inputer ¶
type Inputer interface {
Input() any
}
Inputer is implemented by controllers that expect decoded query or body input. For GET/DELETE/HEAD/OPTIONS the framework decodes URL query parameters (fields tagged `param:"name"`); for POST/PUT/PATCH it decodes the JSON body. To read the query string on POST/PUT/PATCH as well, implement Querier (embed QueryParams). The returned pointer must be stable across the request lifetime (i.e. a field of the controller struct, not a local variable).
type JsonMasker ¶ added in v0.10.0
type JsonMasker interface {
Fields(structure any, tag string) jsonmask.Fields
Mask(src []byte, fields jsonmask.Fields) ([]byte, error)
}
JsonMasker masks sensitive fields in JSON payloads for logging.
type LogOption ¶ added in v0.7.0
type LogOption uint32
LogOption is a bitmask controlling per-endpoint log verbosity.
const ( LogSilent LogOption = 1 << iota // log nothing but 5xx errors; overrides all other bits LogEnter // log when a request arrives LogExit // log when a request completes LogReqBody // log the request body as compacted JSON (masked by `mask` tags) LogReqInput // log the decoded input struct (masked by `mask` tags) LogRespBody // log the response body (masked by `mask` tags) under "respBody" LogRespOutput // log the response body under "result" when LogRespBody is not set )
type MetricReporter ¶ added in v0.11.0
type MetricReporter interface {
ReportMetric(method, path string, statusCode int, dur float64, size int)
}
MetricReporter submits per-request metrics (e.g. to Prometheus).
type MiddlewarePos ¶ added in v0.8.0
type MiddlewarePos int
MiddlewarePos identifies the pipeline position where a middleware executes.
const ( OnRequest MiddlewarePos = iota // first: IP rate limit, request logging OnAuthentication // decode token, populate request context OnAuthorization // check required permissions per endpoint OnHandle // pre-handler: per-user rate limit, audit, tx setup OnSuccessResponse // after a successful response is written; errors are ignored OnErrorResponse // after an error response is written; errors are ignored )
type Option ¶ added in v0.7.0
type Option struct {
// contains filtered or unexported fields
}
Option holds configuration for a Vatel instance.
type OptionalOutput ¶ added in v1.25.0
type OptionalOutput[T any] struct { Out *T }
OptionalOutput is an embeddable generic type for controllers that may or may not produce a response body depending on runtime logic. Set Out to a non-nil pointer to include a body; leave it nil to produce no body (e.g. 204/304).
type FindController struct {
vatel.OptionalOutput[SearchResult]
}
func (c *FindController) Handle(_ vatel.Context) error {
if result, ok := search(); ok {
c.Out = &result
}
return nil
}
func (*OptionalOutput[T]) Result ¶ added in v1.25.0
func (o *OptionalOutput[T]) Result() any
Result implements the Resulter interface. Returns nil when Out is unset, signalling to the framework that no response body should be written.
type Output ¶ added in v1.25.0
type Output[T any] struct { Out T }
Output is an embeddable generic type that provides response serialization for struct-based controllers. Assign Out directly — no pointer needed. The controller struct is already heap-allocated per request, so Out lives inside it at no extra allocation cost.
type GetOrder struct {
vatel.Output[Order]
}
func (c *GetOrder) Handle(_ vatel.Context) error {
c.Out = order
return nil
}
type Paramer ¶
type Paramer interface {
Param() any
}
Paramer is implemented by controllers that bind URL path parameters. The returned pointer must be stable across the request lifetime.
type PathParams ¶ added in v1.25.0
type PathParams[T any] struct { Params T }
PathParams is an embeddable generic type that provides path parameter binding for struct-based controllers. Embed it and access parameters via the Params field.
type GetOrder struct {
api *API
vatel.PathParams[struct {
ID int `param:"id"`
}]
}
// c.Params.ID
func (*PathParams[T]) Param ¶ added in v1.25.0
func (p *PathParams[T]) Param() any
Param implements the Paramer interface for path parameter binding.
type Querier ¶ added in v1.25.0
type Querier interface {
QueryInput() any
}
Querier is implemented by controllers that bind URL query string variables (fields tagged `param:"name"`) independently of the request body. The query string is decoded for every HTTP method, so a POST controller can implement both Querier and Inputer to receive query variables and a JSON body. Embed QueryParams to implement it. The returned pointer must be stable across the request lifetime.
type QueryParams ¶ added in v1.25.0
type QueryParams[T any] struct { Query T }
QueryParams is an embeddable generic type that provides URL query string variable binding for struct-based controllers. Embed it and access query variables via the Query field. It implements Querier, so the query string is decoded for every HTTP method, including POST/PUT/PATCH, and can be combined with Body to receive both query variables and a JSON body.
type ListOrders struct {
api *API
vatel.PathParams[struct{ CustomerID int `param:"customerId"` }]
vatel.QueryParams[struct {
Status string `param:"status"`
Page int `param:"page"`
}]
}
// c.Params.CustomerID, c.Query.Status, c.Query.Page
If T implements Validator, Validate() is called after decoding, before the validation of Body input. Declare Validate() on a named type: an anonymous struct cannot have methods.
func (*QueryParams[T]) QueryInput ¶ added in v1.25.0
func (q *QueryParams[T]) QueryInput() any
QueryInput implements the Querier interface for URL query string binding.
type Resulter ¶
type Resulter interface {
Result() any
}
Resulter is implemented by controllers that produce a JSON response body. Returning nil from Result() suppresses the response body (useful for 304 / 204).
type RevokeTokenChecker ¶ added in v0.3.0
RevokeTokenChecker reports whether an access token has been revoked.
type TokenDecoder ¶
TokenDecoder decodes a raw token into a Tokener.
type Tokener ¶
type Tokener interface {
SystemPayload() map[string]any
ApplicationPayload() AuthPayload
}
Tokener wraps the system and application parts of a decoded JWT.
type Upload ¶ added in v1.25.0
type Upload struct {
Key string // multipart form field name
Header *multipart.FileHeader // populated by BindFile
}
Upload is an embeddable type that provides multipart file upload handling for struct-based controllers. Unlike PathParams/Body, binding is not automatic — call BindFile(ctx) at the start of Handle().
type ImportHandler struct {
vatel.Upload // Upload: vatel.Upload{Key: "file"}
vatel.Output[ImportResult]
}
func (h *ImportHandler) Handle(ctx vatel.Context) error {
if err := h.BindFile(ctx); err != nil { return err }
f, err := h.Open()
if err != nil { return err }
defer f.Close()
_, err = io.Copy(s3Writer, f)
return err
}
func (*Upload) BindFile ¶ added in v1.25.0
BindFile reads the uploaded file from the request and stores it in Header. A missing field or a form that cannot be parsed is a client error: ErrInvalidRequestBody (400, VTL-0007) with field "field" set to Key.
func (*Upload) Open ¶ added in v1.25.0
Open returns the uploaded file as a multipart.File for streaming. The caller is responsible for closing it. Must be called after BindFile.
type Validator ¶ added in v1.25.0
type Validator interface {
Validate() error
}
Validator is an optional interface for input structs. When QueryInput() or Input() returns a value implementing Validator, the framework calls Validate() automatically after all input (path, query, body) is decoded and before invoking Handle(): first on the Querier input, then on the Inputer input. The first non-nil error rejects the request with ErrValidationFailed (400, VTL-0006) wrapping it; the code of a returned *errors.Error, if it has one, replaces VTL-0006. Declare Validate() on a named type: an anonymous struct cannot have methods.
For a WebSocket endpoint, Validate() of the Inputer input is called after the message data is decoded, before Handle(); an error is sent to the client the same way.
Validate() is not called when the endpoint uses WithOptionalBody and the request body is empty: the input stays zero, so a Validate() that fills in default values does not run.
type Vatel ¶
type Vatel struct {
// contains filtered or unexported fields
}
Vatel is the central API gateway. It registers endpoints, configures shared services, and builds fasthttp handlers.
func NewVatel ¶
NewVatel returns a new Vatel instance. The root GET / endpoint (table of contents) is registered automatically.
func (*Vatel) Add ¶
func (v *Vatel) Add(e ...Endpointer)
Add appends endpoints to the registry. Validation happens in BuildHandlers.
func (*Vatel) AddMiddleware ¶ added in v0.3.0
func (v *Vatel) AddMiddleware(pos MiddlewarePos, f ...func(Context, EndpointMeta) error)
AddMiddleware registers middleware at the given pipeline position. Middleware functions are called in the order they are added.
func (*Vatel) BuildHandlers ¶
BuildHandlers registers all endpoints with the router. It returns an error instead of panicking: an invalid endpoint (e.g. a Param(), QueryInput() or GET Input() that does not return a pointer to a struct), a path that does not begin with "/", a route the router rejects (a duplicate or conflicting path, or an own GET "/", which is taken by the built-in endpoint list), a WS endpoint without WithWebsocketSupport or a duplicate WS path.
Middleware (AddMiddleware) and endpoints (Add) must be added before BuildHandlers: it copies them, later additions have no effect.
BuildHandlers may be called only once. Registration is not transactional: after a failed build the router may already hold some of the routes and they cannot be removed, so the Vatel is unusable. A further BuildHandlers or ListenAndServe returns a "previous build failed" error; create a new Vatel and router instead.
func (*Vatel) ListenAndServe ¶ added in v1.25.0
ListenAndServe compiles all endpoints (unless BuildHandlers was already called), starts the HTTP server on addr, and blocks until the server stops. The caller retains ownership of mux so that non-vatel routes can be added before calling this method. The server is configured with DefaultIdleTimeout and CloseOnShutdown; see WithServerConfig.
Use Shutdown to stop the server gracefully; ListenAndServe then returns nil. Called after Shutdown, it returns ErrServerClosed without starting.
func (*Vatel) MustBuildHandlers ¶
MustBuildHandlers registers all endpoints with the router. Panics on error.
func (*Vatel) Shutdown ¶ added in v1.25.0
Shutdown gracefully stops the server:
- It starts closing all active WebSocket connections (clients receive a "going away" frame and can reconnect; see WebsocketVatel.Shutdown) and stops accepting new connections. ListenAndServe returns nil; called later, it returns ErrServerClosed.
- It waits until no HTTP request is in flight and every WebSocket connection has closed. Requests being handled and requests still being received (headers or body being read) finish normally, and their responses carry "Connection: close". Idle keep-alive connections do not hold it up: they are closed. A new connection that has sent nothing yet gets about 5 seconds to start its request (as in net/http), then it is closed too. A WebSocket message being handled is finished; a WebSocket client that does not answer the close frame is disconnected after about one second.
- It cancels the request contexts (Context.Context()) and returns nil.
When ctx is done before step 2 ends, the request contexts of requests still in flight are cancelled at once, so handlers listening to them can stop (a context.Canceled error gives 503). Shutdown then waits up to one more second for them to finish and returns ctx.Err(); requests still running after that are not interrupted, and their responses carry "Connection: close".
Without a server started by ListenAndServe the request contexts are cancelled at once, and Shutdown waits, within ctx, only for the WebSocket connections. Requests that start after Shutdown get a cancelled context. Calling Shutdown again, also concurrently, is safe: each call waits with its own ctx.
Pass a context with a deadline to bound the graceful phase:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() v.Shutdown(ctx)
type VatelContext ¶
type VatelContext struct {
// contains filtered or unexported fields
}
VatelContext is the concrete Context implementation wrapping fasthttp.RequestCtx.
func (*VatelContext) BodyWriter ¶
func (c *VatelContext) BodyWriter() io.Writer
func (*VatelContext) Context ¶ added in v1.25.0
func (c *VatelContext) Context() context.Context
func (*VatelContext) FormFile ¶
func (c *VatelContext) FormFile(key string) (*multipart.FileHeader, error)
func (*VatelContext) FormValue ¶
func (c *VatelContext) FormValue(key string) []byte
func (*VatelContext) Get ¶ added in v0.7.0
func (c *VatelContext) Get(key string) any
func (*VatelContext) Header ¶
func (c *VatelContext) Header(name string) []byte
func (*VatelContext) PathValue ¶ added in v1.25.0
func (c *VatelContext) PathValue(name string) string
func (*VatelContext) QueryValue ¶ added in v1.25.0
func (c *VatelContext) QueryValue(name string) []byte
func (*VatelContext) RemoteAddr ¶ added in v1.25.0
func (c *VatelContext) RemoteAddr() string
func (*VatelContext) RequestBody ¶ added in v1.25.0
func (c *VatelContext) RequestBody() []byte
func (*VatelContext) RequestTime ¶ added in v1.25.0
func (c *VatelContext) RequestTime() time.Time
func (*VatelContext) SaveMultipartFile ¶
func (c *VatelContext) SaveMultipartFile(fh *multipart.FileHeader, path string) error
func (*VatelContext) SetContentType ¶
func (c *VatelContext) SetContentType(ct []byte) Context
func (*VatelContext) SetHeader ¶
func (c *VatelContext) SetHeader(name, val []byte) Context
func (*VatelContext) SetStatusCode ¶
func (c *VatelContext) SetStatusCode(code int) Context
func (*VatelContext) SetTokenPayload ¶
func (c *VatelContext) SetTokenPayload(tp any)
func (*VatelContext) SetValue ¶ added in v1.25.0
func (c *VatelContext) SetValue(key, val any) Context
func (*VatelContext) SetVerboseError ¶ added in v1.25.0
func (c *VatelContext) SetVerboseError(v bool)
func (*VatelContext) TokenPayload ¶
func (c *VatelContext) TokenPayload() any
func (*VatelContext) VerboseError ¶ added in v1.25.0
func (c *VatelContext) VerboseError() bool
func (*VatelContext) VisitUserValues ¶ added in v0.7.0
func (c *VatelContext) VisitUserValues(f func(key []byte, val any))
type WebsocketContext ¶ added in v0.13.0
type WebsocketContext interface {
BodyWriter() io.Writer
TokenPayload() any
SetTokenPayload(tp any)
Set(key string, val any) WebsocketContext
Get(key string) any
VisitValues(func(key []byte, val any))
Created() time.Time
ID() uint64
}
WebsocketContext is the per-message interface passed to WebSocket handlers and middleware.
func NewWsContext ¶ added in v0.13.0
func NewWsContext(c *websocket.Conn) WebsocketContext
NewWsContext creates a WebsocketContext for the given connection.
type WebsocketHandler ¶ added in v0.13.0
type WebsocketHandler interface {
Handle(WebsocketContext) error
}
WebsocketHandler is the core interface for WebSocket message controllers.
type WebsocketRequest ¶ added in v0.13.5
type WebsocketRequest struct {
Path string `json:"path"`
Data json.RawMessage `json:"data,omitempty"`
}
WebsocketRequest is the envelope for all incoming WebSocket messages.
type WebsocketVatel ¶ added in v0.13.5
type WebsocketVatel struct {
// contains filtered or unexported fields
}
WebsocketVatel manages WebSocket endpoint registration and message routing.
func NewWebsocketVatel ¶ added in v0.13.5
func NewWebsocketVatel(optFunc ...func(*WsOption)) *WebsocketVatel
NewWebsocketVatel creates a WebsocketVatel and registers the built-in updateAccessToken endpoint.
func (*WebsocketVatel) CloseConn ¶ added in v1.25.0
func (wv *WebsocketVatel) CloseConn(cid uint64, status websocket.StatusCode, reason string) bool
CloseConn closes the live connection cid with status and reason, e.g. when its user was removed from the organization, logged out, or its token was revoked; its further messages are not served (the message being handled is finished), and OnClose is called once it has closed. Reason is sent to the client: it is cut to 123 bytes and must not carry sensitive details. It reports false if there is no such connection or it is already closing. It does not wait for the close and is safe from any goroutine, including a VisitConns callback.
func (*WebsocketVatel) Endpoints ¶ added in v0.13.5
func (wv *WebsocketVatel) Endpoints() []Endpoint
Endpoints returns the built-in WebSocket endpoints exposed by WebsocketVatel.
func (*WebsocketVatel) OnClose ¶ added in v0.13.5
func (wv *WebsocketVatel) OnClose(f func(*websocket.Conn, error))
OnClose registers a callback invoked when a WebSocket connection closes.
func (*WebsocketVatel) OnOpen ¶ added in v0.13.5
func (wv *WebsocketVatel) OnOpen(f func(*websocket.Conn))
OnOpen registers a callback invoked when a new WebSocket connection is established.
func (*WebsocketVatel) OnTokenUpdate ¶ added in v1.25.0
func (wv *WebsocketVatel) OnTokenUpdate(f func(cid uint64, prev, next AuthPayload) error)
OnTokenUpdate registers a callback invoked by UpdateAccessToken after the token is decoded and before its payload next is stored for the connection cid; prev is the payload stored for it so far, nil for the first token. Returning an error rejects the token: the payload is not stored (the connection keeps its previous payload, if any) and the error is returned from UpdateAccessToken, and so sent to the client of the updateAccessToken endpoint. A *WsCloseError then also closes the connection after the reply, e.g. to keep one security context (user, organization) per connection:
wv.OnTokenUpdate(func(_ uint64, prev, next vatel.AuthPayload) error {
if prev == nil {
return nil // first token
}
p, n := prev.(*Claims), next.(*Claims)
switch {
case p.UserID != n.UserID:
return vatel.NewWsCloseError(vatel.WsStatusForbidden, "user changed", nil)
case p.OrgID != n.OrgID:
return vatel.NewWsCloseError(vatel.WsStatusContextChanged, "organization changed", nil)
}
return nil
})
The callback runs without a lock held; if another payload is stored for cid meanwhile (a concurrent UpdateAccessToken), or the connection closes, UpdateAccessToken returns ErrWsTokenConflict and stores nothing.
func (*WebsocketVatel) RegisterEndpoint ¶ added in v0.13.5
RegisterEndpoint is called by Vatel.BuildHandlers (registerEndpoints) for every endpoint with method "WS".
func (*WebsocketVatel) Shutdown ¶ added in v1.25.0
func (wv *WebsocketVatel) Shutdown()
Shutdown closes all active WebSocket connections with a "going away" status, so clients can reconnect to another instance; their further messages are not served, the message being handled is finished (its writes after the close fail), and a connection opened later is closed at once. It does not wait for the connections to close: a client that does not answer the close frame (or does not read) is disconnected after about one second. Vatel.Shutdown calls it and waits, within its context, until every connection has closed; calling it again does nothing.
func (*WebsocketVatel) UpdateAccessToken ¶ added in v0.13.5
func (wv *WebsocketVatel) UpdateAccessToken(cid uint64, token string) error
UpdateAccessToken decodes a new access token for the connection cid and calls the OnTokenUpdate callback, if any, with the payload stored so far; unless the callback returns an error, it stores the new payload for cid. If the connection is closing, or its payload has expired (the connection is then closed with WsStatusAuthRequired, "token expired", even if its expiry timer has not fired yet), it returns ErrWsTokenConflict at once: the token is not decoded and the callback is not called. A token that cannot be decoded, or whose payload (Expirer) has expired, is rejected with ErrUnauthorized; if another payload was stored for cid while the callback ran, or the connection closed, started closing or its payload expired, it returns ErrWsTokenConflict. A rejected token is not stored: the connection keeps its payload. Storing a payload arms the expiry of the connection (Expirer) and ends its WithWsAuthTimeout. A payload stored for a cid without a live connection becomes the payload of the connection opened with that ID (UpgradeWithID), unless it has expired by then: the connection then opens without a payload. Without WithTokenDecoder it does nothing.
A *WsCloseError returned by the callback is returned as is: the updateAccessToken endpoint then closes the connection after its reply; a direct caller closes it with CloseConn.
func (*WebsocketVatel) Upgrade ¶ added in v0.13.5
func (wv *WebsocketVatel) Upgrade(ctx *fasthttp.RequestCtx)
Upgrade upgrades an HTTP connection to WebSocket.
func (*WebsocketVatel) UpgradeWithID ¶ added in v0.13.5
func (wv *WebsocketVatel) UpgradeWithID(ctx *fasthttp.RequestCtx, id uint64)
UpgradeWithID upgrades an HTTP connection to WebSocket with a caller-supplied ID. The ID must not be the ID of a live connection: such a connection is closed at once with a "policy violation" status (1008).
func (*WebsocketVatel) VisitConns ¶ added in v1.25.0
func (wv *WebsocketVatel) VisitConns(f func(cid uint64, tp AuthPayload) bool)
VisitConns calls f for each live connection (not being closed), in the order of IDs, with its ID and its token payload (nil without one), until f returns false. f is called on a snapshot without a lock held, so it may call CloseConn; a connection may close meanwhile. Use it to find the connections of a user or an organization from their payloads.
type WsCloseError ¶ added in v1.25.0
type WsCloseError struct {
Code websocket.StatusCode
Reason string
Err error // optional
}
WsCloseError is an error that closes the WebSocket connection. When the handler of a message (including the built-in updateAccessToken, through OnTokenUpdate, and the WithWsAuthFunc function) returns an error with a *WsCloseError in its chain, the client first gets the error reply to the message, then the connection is closed with Code and Reason.
The reply is built from Err when its chain has an *errors.Error with a status (other errors joined or wrapped next to the WsCloseError do not count); otherwise from Code: WsStatusAuthRequired gives ErrUnauthorized (401), WsStatusForbidden ErrForbidden (403), WsStatusContextChanged ErrWsContextChanged (409), any other code 400. It is never alarmed.
Reason is sent to the client in the close frame, cut to 123 bytes: it must not carry sensitive details; put them in Err.
func NewWsCloseError ¶ added in v1.25.0
func NewWsCloseError(code websocket.StatusCode, reason string, err error) *WsCloseError
NewWsCloseError returns a WsCloseError closing the connection with code and reason; err (may be nil) is the cause.
func (*WsCloseError) Error ¶ added in v1.25.0
func (e *WsCloseError) Error() string
func (*WsCloseError) Unwrap ¶ added in v1.25.0
func (e *WsCloseError) Unwrap() error
type WsContext ¶ added in v0.13.0
type WsContext struct {
// contains filtered or unexported fields
}
WsContext is the concrete WebsocketContext implementation.
func (*WsContext) BodyWriter ¶ added in v0.13.0
BodyWriter returns the writer of messages to the client. A write after the connection is closed fails with websocket.ErrConnClosed; a write to a client that does not read fails after the write timeout (WithWsWriteTimeout).
func (*WsContext) Set ¶ added in v0.13.0
func (c *WsContext) Set(key string, val any) WebsocketContext
func (*WsContext) SetTokenPayload ¶ added in v0.13.0
func (*WsContext) TokenPayload ¶ added in v0.13.0
func (*WsContext) VisitValues ¶ added in v0.13.0
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package auth provides JWT-based authentication and authorization middleware for vatel HTTP endpoints.
|
Package auth provides JWT-based authentication and authorization middleware for vatel HTTP endpoints. |
|
Package main demonstrates a complete vatel API server setup.
|
Package main demonstrates a complete vatel API server setup. |
|
Package jsonmask masks sensitive values in JSON documents according to struct tags.
|
Package jsonmask masks sensitive values in JSON documents according to struct tags. |