Documentation
¶
Overview ¶
Package problem implements RFC 9457 problem details with stable codes that clients localize on.
A problem's type is either "about:blank", meaning nothing beyond its HTTP status, or a Type defined in a Registry, served as a relative URI such as "/problems/shop/name_taken". Every defined type has a Code, a short snake_case name that clients use as a message key. For an about:blank problem, clients derive the code from the status with CodeForStatus.
Index ¶
- Constants
- Variables
- func Denied(err error) bool
- func Handler(registries ...*Registry) http.Handler
- func Pages(opts PagesOptions) http.Handler
- func Pointer(path ...any) string
- func Write(w http.ResponseWriter, p *Problem)
- type Catalog
- type Code
- type Doc
- type Docs
- type FieldError
- type Link
- type Localizer
- type PagesOptions
- type Problem
- func (p *Problem) AsDenial() *Problem
- func (p *Problem) ContentType(string) string
- func (p *Problem) Error() string
- func (p *Problem) GetStatus() int
- func (p *Problem) Key() Code
- func (p Problem) MarshalJSON() ([]byte, error)
- func (p *Problem) Resolve() *Problem
- func (p *Problem) Unwrap() error
- func (p *Problem) With(key string, value any) *Problem
- func (p *Problem) WithCause(err error) *Problem
- type Registry
- type Type
Constants ¶
const Blank = "about:blank"
Blank is the problem type for a problem with no semantics beyond its status.
const MediaType = "application/problem+json"
MediaType is the RFC 9457 media type for a problem details object.
Variables ¶
var ( Invalid = rule("invalid", "Invalid value", "The value is not valid for this field.") Required = rule("required", "Value required", "The field is required and was missing or empty.") TooShort = rule("too_short", "Value too short", "The value has fewer characters than the field allows.", "min") TooLong = rule("too_long", "Value too long", "The value has more characters than the field allows.", "max") TooFew = rule("too_few", "Too few items", "The list has fewer items than the field allows.", "min") TooMany = rule("too_many", "Too many items", "The list has more items than the field allows.", "max") BelowMinimum = rule("below_minimum", "Number too small", "The number is below the field's minimum. exclusive is \"true\" when the minimum itself is not allowed.", "min", "exclusive") AboveMaximum = rule("above_maximum", "Number too large", "The number is above the field's maximum. exclusive is \"true\" when the maximum itself is not allowed.", "max", "exclusive") InvalidFormat = rule("invalid_format", "Invalid format", "The value does not have the field's format, such as an email address, a URI or a date.") InvalidChoice = rule("invalid_choice", "Not an allowed value", "The value is not one of the field's allowed values, listed in allowed.", "allowed") InvalidType = rule("invalid_type", "Wrong type", "The value has the wrong JSON type, such as a string where a number belongs.") UnexpectedField = rule("unexpected_field", "Unknown field", "The request has a field the operation does not accept.") DuplicateItems = rule("duplicate_items", "Duplicate items", "The list has the same item more than once.") )
The rules a field can fail. They are shared by every application, so their URIs are /problems/<code>. An application whose field fails a check of its own uses one of its Types instead.
var Rules = []*Type{ Invalid, Required, TooShort, TooLong, TooFew, TooMany, BelowMinimum, AboveMaximum, InvalidFormat, InvalidChoice, InvalidType, UnexpectedField, DuplicateItems, }
Rules lists every rule, in the order above.
sub, err := fs.Sub(sharedFS, "messages") if err != nil { panic(err) } return MustCatalog(sub) }()
Shared holds the messages for the codes every API shares: the status codes, validation and its rules, and the client-side network and unknown.
sub, err := fs.Sub(sharedDocsFS, "docs") if err != nil { panic(err) } return MustDocs(sub) }()
SharedDocs holds the guidance for the shared codes' pages.
var StatusCodes = []Code{ InvalidRequest, Unauthenticated, Forbidden, NotFound, Conflict, RateLimited, Internal, Unavailable, }
StatusCodes lists the codes CodeForStatus returns.
var Validation = rule("validation", "Invalid request",
"One or more fields in the request are invalid. Each entry in errors names the field and the rule it failed.")
Validation is the problem for a request with invalid fields. Its Errors list each one, typed with a rule.
Functions ¶
func Pages ¶ added in v0.11.0
func Pages(opts PagesOptions) http.Handler
Pages serves the documentation pages problem type URIs resolve to: /problems/ lists every type, /problems/<code> documents a shared one, and /problems/<name>/<code> a type in the registry called name. Each page is in the reader's language (?lang= overrides it, so a shared link keeps its language) and shows the message readers see, why it happens, how to fix it, and the technical details. Mount it at /problems/ on the API's host.
Types ¶
type Catalog ¶ added in v0.11.0
type Catalog struct {
// contains filtered or unexported fields
}
Catalog holds the messages for problem codes, by language: ICU MessageFormat strings keyed by code, as clients' catalogs are.
func MustCatalog ¶ added in v0.11.0
MustCatalog is NewCatalog that panics, for catalogs embedded in the binary.
func NewCatalog ¶ added in v0.11.0
NewCatalog reads one JSON file per language from fsys, named for the language, such as en.json and ja.json, each an object of messages keyed by code. Every message must be valid; English is required.
func (*Catalog) Has ¶ added in v0.11.0
Has reports whether the catalog has an English message for code.
type Code ¶
type Code string
Code is a problem type's stable, machine-readable name. Clients key their messages on it, so a code is part of the API contract: never rename one. The set is open: a client that meets a code it doesn't know falls back to the one for the status.
const ( InvalidRequest Code = "invalid_request" Unauthenticated Code = "unauthenticated" Forbidden Code = "forbidden" NotFound Code = "not_found" Conflict Code = "conflict" RateLimited Code = "rate_limited" Internal Code = "internal" )
The codes clients derive from the status of an about:blank problem. They are never sent, since about:blank has no members beyond RFC 9457's own.
func CodeForStatus ¶
CodeForStatus is the code for an about:blank problem with the given status.
type Doc ¶ added in v0.11.0
type Doc struct {
// Title replaces the type's English title on its page.
Title string `json:"title,omitempty"`
// Why explains what causes the problem.
Why string `json:"why,omitempty"`
// Fix lists the steps that resolve it, in order.
Fix []string `json:"fix,omitempty"`
// Links point to further reading.
Links []Link `json:"links,omitempty"`
}
Doc is the guidance a problem type's page shows: what it means, why it happens and how to fix it.
type Docs ¶ added in v0.11.0
type Docs struct {
// contains filtered or unexported fields
}
Docs holds Doc entries for problem codes, by language.
type FieldError ¶
type FieldError struct {
Type string `json:"type" format:"uri-reference" doc:"The rule's problem type."`
Code Code `` /* 160-byte string literal not displayed */
Detail string `json:"detail,omitempty" doc:"Explanation, in the language the Content-Language header names; English without it."`
Pointer string `json:"pointer,omitempty" doc:"JSON Pointer to the invalid body field, as a URI fragment such as #/items/0/name."`
Parameter string `json:"parameter,omitempty" doc:"Name of the invalid request parameter."`
In string `json:"in,omitempty" enum:"query,path,header,cookie" doc:"Where parameter is."`
Params map[string]any `json:"params,omitempty" doc:"Values the rule's localized message shows."`
// contains filtered or unexported fields
}
FieldError is one entry in a validation problem's errors, the extension RFC 9457 §3 uses in its validation example. Pointer locates a field in the request body; Parameter and In locate a query, path, header or cookie parameter instead.
func (*FieldError) Error ¶
func (e *FieldError) Error() string
Error makes a FieldError an error, so a huma Resolver can return one.
func (FieldError) MarshalJSON ¶ added in v0.7.0
func (e FieldError) MarshalJSON() ([]byte, error)
MarshalJSON encodes a field error missing a param its type's message needs as Invalid, so a client never shows a message with a placeholder it cannot fill.
func (*FieldError) With ¶
func (e *FieldError) With(key string, value any) *FieldError
With adds a value the field error's localized message shows.
type Localizer ¶ added in v0.11.0
type Localizer struct {
// Locale picks the reader's locale, such as "ja" or "es-MX", from the
// request's headers. Nil means Accept-Language's first language.
Locale func(http.Header) string
// Catalogs hold the app's own codes; the first with a code wins. Shared
// is always consulted after them.
Catalogs []*Catalog
}
Localizer writes problems in the reader's language: each one's detail, and each field error's, becomes the catalog's message for its code.
func (*Localizer) Language ¶ added in v0.11.0
Language is the language l writes for a request: the base of its locale when a catalog has it, else English.
func (*Localizer) Localize ¶ added in v0.11.0
Localize returns p as clients receive it (see Resolve), in lang. A problem of a defined type, and each field error, gets its code's message as its detail. An about:blank problem keeps a detail the server wrote when lang is English, since that is the language servers write in, and otherwise gets its status's message, so the reader never sees two languages. p is not modified.
type PagesOptions ¶ added in v0.11.0
type PagesOptions struct {
// Registries are the application's problem types, documented beside the
// shared ones.
Registries []*Registry
// Localizer picks the reader's language and renders each type's message.
// Nil uses Accept-Language and the shared messages only.
Localizer *Localizer
// Docs hold the application's guidance; SharedDocs is consulted after them.
Docs []*Docs
}
PagesOptions configure Pages.
type Problem ¶
type Problem struct {
Type string `` /* 148-byte string literal not displayed */
Title string `json:"title,omitempty" doc:"Short English summary of the problem type, for developers."`
Status int `json:"status,omitempty" doc:"The HTTP status code."`
Detail string `` /* 212-byte string literal not displayed */
Instance string `json:"instance,omitempty" format:"uri-reference" doc:"Identifies this occurrence of the problem."`
Code Code `` /* 222-byte string literal not displayed */
Params map[string]any `json:"params,omitempty" doc:"Values the code's localized message shows."`
Errors []*FieldError `json:"errors,omitempty" doc:"For a validation problem, each invalid field and the rule it failed."`
// contains filtered or unexported fields
}
Problem is an RFC 9457 problem details object. Title is English. Detail and each FieldError's Detail are English as written; a Localizer rewrites them in the reader's language and says which with Content-Language. Clients that get no Content-Language show their own message for Code instead.
func ValidationFailed ¶
func ValidationFailed(errs ...*FieldError) *Problem
ValidationFailed returns a Validation problem listing errs.
func (*Problem) AsDenial ¶
AsDenial marks p as a refusal to authorize, so callers can tell it from a failure to work out the answer, such as an unreachable database, which must never be reported as a permission problem. It changes nothing sent.
func (*Problem) ContentType ¶
ContentType makes huma serve a Problem as application/problem+json.
func (*Problem) Key ¶
Key is the code clients localize on: Code, or for an about:blank problem, the code its status implies.
func (Problem) MarshalJSON ¶ added in v0.7.0
MarshalJSON encodes the problem as Resolve returns it.
func (*Problem) Resolve ¶ added in v0.7.0
Resolve returns p as clients receive it. A problem missing a param its type's message needs is sent as the about:blank problem for its status, so a client never shows a message with a placeholder it cannot fill. MarshalJSON sends the resolved problem.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the problem types of one application. Their URIs are /problems/<name>/<code>, so a host that mounts several applications serves all their pages without collisions.
func NewRegistry ¶
NewRegistry returns an empty registry for the application name, such as "shop". It panics if name is not a lowercase URI segment.
func (*Registry) Define ¶
Define adds a type to the registry and returns it. Call it from package variable declarations: it panics on an invalid or duplicate code, or on one that collides with the codes this package defines.
type Type ¶
type Type struct {
Code Code
Status int
// Title is a short English summary that never varies between occurrences.
Title string
// Doc explains, in English, what causes the problem and how to resolve it.
Doc string
// Params names the values the type's localized message shows.
Params []string
// contains filtered or unexported fields
}
Type is a problem type (RFC 9457 §4): a URI, a short title and the status it is used with. Define one with Registry.Define.
func (*Type) At ¶
func (t *Type) At(pointer, detail string) *FieldError
At returns a field error of this type for the body field at pointer, which Pointer builds.
func (*Type) AtParameter ¶
func (t *Type) AtParameter(in, name, detail string) *FieldError
AtParameter returns a field error of this type for a request parameter. in is "query", "path", "header" or "cookie", as in OpenAPI.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package humaproblem makes huma produce problem details: every error huma builds, including request validation, becomes a *problem.Problem, and the OpenAPI document lists the codes clients localize on.
|
Package humaproblem makes huma produce problem details: every error huma builds, including request validation, becomes a *problem.Problem, and the OpenAPI document lists the codes clients localize on. |
|
Package problemrules holds go-ruleguard rules that keep internal errors out of problem responses.
|
Package problemrules holds go-ruleguard rules that keep internal errors out of problem responses. |
|
Package problemtest checks that a client's message catalog covers an application's problem types.
|
Package problemtest checks that a client's message catalog covers an application's problem types. |