Documentation
¶
Overview ¶
Package hproblem provides a standard interface for handling API error responses in web applications. It implements RFC 7807 (Problem Details for HTTP APIs) which specifies a way to carry machine-readable details of errors in a HTTP response to avoid the need to define new error response formats for HTTP APIs.
Index ¶
- Constants
- Variables
- func Errorf(statusCode int, format string, a ...interface{}) error
- func MethodNotAllowed(w http.ResponseWriter, r *http.Request)
- func NotFound(w http.ResponseWriter, r *http.Request)
- func ServeError(w http.ResponseWriter, r *http.Request, err error)
- func StatusCode(err error) int
- func Wrap(statusCode int, err error) error
- type DetailsError
Examples ¶
Constants ¶
const ( StatusContinue statusError = 100 // RFC 9110, 15.2.1 StatusSwitchingProtocols statusError = 101 // RFC 9110, 15.2.2 StatusProcessing statusError = 102 // RFC 2518, 10.1 StatusEarlyHints statusError = 103 // RFC 8297 StatusOK statusError = 200 // RFC 9110, 15.3.1 StatusCreated statusError = 201 // RFC 9110, 15.3.2 StatusAccepted statusError = 202 // RFC 9110, 15.3.3 StatusNonAuthoritativeInfo statusError = 203 // RFC 9110, 15.3.4 StatusNoContent statusError = 204 // RFC 9110, 15.3.5 StatusResetContent statusError = 205 // RFC 9110, 15.3.6 StatusPartialContent statusError = 206 // RFC 9110, 15.3.7 StatusMultiStatus statusError = 207 // RFC 4918, 11.1 StatusAlreadyReported statusError = 208 // RFC 5842, 7.1 StatusIMUsed statusError = 226 // RFC 3229, 10.4.1 StatusMultipleChoices statusError = 300 // RFC 9110, 15.4.1 StatusMovedPermanently statusError = 301 // RFC 9110, 15.4.2 StatusFound statusError = 302 // RFC 9110, 15.4.3 StatusSeeOther statusError = 303 // RFC 9110, 15.4.4 StatusNotModified statusError = 304 // RFC 9110, 15.4.5 StatusUseProxy statusError = 305 // RFC 9110, 15.4.6 StatusTemporaryRedirect statusError = 307 // RFC 9110, 15.4.8 StatusPermanentRedirect statusError = 308 // RFC 9110, 15.4.9 StatusBadRequest statusError = 400 // RFC 9110, 15.5.1 StatusPaymentRequired statusError = 402 // RFC 9110, 15.5.3 StatusForbidden statusError = 403 // RFC 9110, 15.5.4 StatusNotFound statusError = 404 // RFC 9110, 15.5.5 StatusMethodNotAllowed statusError = 405 // RFC 9110, 15.5.6 StatusNotAcceptable statusError = 406 // RFC 9110, 15.5.7 StatusProxyAuthRequired statusError = 407 // RFC 9110, 15.5.8 StatusRequestTimeout statusError = 408 // RFC 9110, 15.5.9 StatusConflict statusError = 409 // RFC 9110, 15.5.10 StatusGone statusError = 410 // RFC 9110, 15.5.11 StatusLengthRequired statusError = 411 // RFC 9110, 15.5.12 StatusPreconditionFailed statusError = 412 // RFC 9110, 15.5.13 StatusRequestEntityTooLarge statusError = 413 // RFC 9110, 15.5.14 StatusRequestURITooLong statusError = 414 // RFC 9110, 15.5.15 StatusUnsupportedMediaType statusError = 415 // RFC 9110, 15.5.16 StatusRequestedRangeNotSatisfiable statusError = 416 // RFC 9110, 15.5.17 StatusExpectationFailed statusError = 417 // RFC 9110, 15.5.18 StatusTeapot statusError = 418 // RFC 9110, 15.5.19 (Unused) StatusMisdirectedRequest statusError = 421 // RFC 9110, 15.5.20 StatusUnprocessableEntity statusError = 422 // RFC 9110, 15.5.21 StatusLocked statusError = 423 // RFC 4918, 11.3 StatusFailedDependency statusError = 424 // RFC 4918, 11.4 StatusTooEarly statusError = 425 // RFC 8470, 5.2. StatusUpgradeRequired statusError = 426 // RFC 9110, 15.5.22 StatusPreconditionRequired statusError = 428 // RFC 6585, 3 StatusTooManyRequests statusError = 429 // RFC 6585, 4 StatusRequestHeaderFieldsTooLarge statusError = 431 // RFC 6585, 5 StatusInternalServerError statusError = 500 // RFC 9110, 15.6.1 StatusNotImplemented statusError = 501 // RFC 9110, 15.6.2 StatusBadGateway statusError = 502 // RFC 9110, 15.6.3 StatusGatewayTimeout statusError = 504 // RFC 9110, 15.6.5 StatusHTTPVersionNotSupported statusError = 505 // RFC 9110, 15.6.6 StatusVariantAlsoNegotiates statusError = 506 // RFC 2295, 8.1 StatusInsufficientStorage statusError = 507 // RFC 4918, 11.5 StatusLoopDetected statusError = 508 // RFC 5842, 7.2 StatusNotExtended statusError = 510 // RFC 2774, 7 StatusNetworkAuthenticationRequired statusError = 511 // RFC 6585, 6 )
HTTP status codes as registered with IANA. See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
Variables ¶
var ErrInvalidEncoding = errors.New("hproblem: invalid details error encoding")
Functions ¶
func MethodNotAllowed ¶
func MethodNotAllowed(w http.ResponseWriter, r *http.Request)
MethodNotAllowed replies to the request with StatusMethodNotAllowed.
func NotFound ¶
func NotFound(w http.ResponseWriter, r *http.Request)
NotFound replies to the request with StatusNotFound.
func ServeError ¶
func ServeError(w http.ResponseWriter, r *http.Request, err error)
ServeError replies to the request by rendering err. If err implements http.Handler, its ServeHTTP method is called. Otherwise, err is rendered as JSON, XML or plain text depending on the request's Accept header. If err is nil, it will be rendered as StatusOK.
Example (Json) ¶
package main import ( "fmt" "io" "net/http/httptest" "os" "github.com/askeladdk/hproblem" ) func main() { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) r.Header.Set("Accept", "application/json") hproblem.ServeError(w, r, hproblem.StatusBadRequest) fmt.Println(w.Result().Status) _, _ = io.Copy(os.Stdout, w.Body) }
Output: 400 Bad Request {"detail":"Bad Request","status":400,"title":"Bad Request"}
Example (Text) ¶
package main import ( "fmt" "io" "net/http/httptest" "os" "github.com/askeladdk/hproblem" ) func main() { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) hproblem.ServeError(w, r, hproblem.StatusBadRequest) fmt.Println(w.Result().Status) _, _ = io.Copy(os.Stdout, w.Body) }
Output: 400 Bad Request Bad Request
Example (Xml) ¶
package main import ( "fmt" "io" "net/http/httptest" "os" "github.com/askeladdk/hproblem" ) func main() { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) r.Header.Set("Accept", "text/xml") hproblem.ServeError(w, r, hproblem.StatusBadRequest) fmt.Println(w.Result().Status) _, _ = io.Copy(os.Stdout, w.Body) }
Output: 400 Bad Request <?xml version="1.0" encoding="UTF-8"?> <problem xmlns="urn:ietf:rfc:7807"><detail>Bad Request</detail><status>400</status><title>Bad Request</title></problem>
func StatusCode ¶
StatusCode reports the HTTP status code associated with err if it implements the StatusCode() int method, 504 Gateway Timeout if it implements Timeout() bool, 503 Service Unavailable if it implements Temporary() bool, 500 Internal Server Error otherwise, or 200 OK if err is nil. StatusCode will unwrap err to find the most precise status code.
Example ¶
package main import ( "fmt" "io" "net/http" "github.com/askeladdk/hproblem" ) func main() { fmt.Println(hproblem.StatusCode(nil)) fmt.Println(hproblem.StatusCode(io.EOF)) fmt.Println(hproblem.StatusCode(hproblem.Wrap(http.StatusBadRequest, io.EOF))) }
Output: 200 500 400
Types ¶
type DetailsError ¶
type DetailsError struct { // A human-readable explanation specific to this occurrence of the problem. Detail string `json:"detail,omitempty" xml:"detail,omitempty"` // A URI reference that identifies the specific occurrence of the problem. // It may or may not yield further information if dereferenced. Instance string `json:"instance,omitempty" xml:"instance,omitempty"` // The HTTP status code ([RFC7231], Section 6) // generated by the origin server for this occurrence of the problem. Status int `json:"status,omitempty" xml:"status,omitempty"` // A short, human-readable summary of the problem // type. It SHOULD NOT change from occurrence to occurrence of the // problem, except for purposes of localization (e.g., using // proactive content negotiation; see [RFC7231], Section 3.4). Title string `json:"title,omitempty" xml:"title,omitempty"` // A URI reference [RFC3986] that identifies the // problem type. This specification encourages that, when // dereferenced, it provide human-readable documentation for the // problem type (e.g., using HTML [W3C.REC-html5-20141028]). When // this member is not present, its value is assumed to be // "about:blank". Type string `json:"type,omitempty" xml:"type,omitempty"` // XMLName is needed to marshal to XML. XMLName xml.Name `json:"-" xml:"urn:ietf:rfc:7807 problem"` // contains filtered or unexported fields }
DetailsError implements the RFC 7807 model. See: https://datatracker.ietf.org/doc/html/rfc7807
Additional fields can be added by embedding it inside another struct.
type TraceDetailsError struct { *hproblem.DetailsError TraceID string `json:"trace_id" xml:"trace_id"` } hproblem.ServeError(w, r, TraceDetailsError{})
func NewDetailsError ¶
func NewDetailsError(err error) *DetailsError
NewDetailsError returns a new DetailsError with the Detail, Status and Title fields set according to err.
func (*DetailsError) Error ¶
func (details *DetailsError) Error() string
Error implements the error interface and returns the Detail field.
func (*DetailsError) StatusCode ¶
func (details *DetailsError) StatusCode() int
StatusCode implements the interface used by StatusCode and returns the Status field.
func (*DetailsError) Unmarshal ¶ added in v0.0.2
func (details *DetailsError) Unmarshal(data []byte) error
Unmarshal parses a JSON or XML encoded details error. Returns ErrInvalidEncoding if the encoding is invalid.
func (*DetailsError) Unwrap ¶
func (details *DetailsError) Unwrap() error
Unwrap implements the interface used by errors.Unwrap() and returns the wrapped error.