httpsyproblem

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2021 License: ISC Imports: 6 Imported by: 1

README

HTT-Peasy Problem

GoDoc Go Report Card

Overview

Package httpsyproblem provides a standard interface for handling API error responses in web applications. It implements RFC 7807 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.

Install

go get -u github.com/askeladdk/httpsyproblem

Quickstart

The two basic functions are Wrap and Error. Wrap associates an error with a status code. Error replies to requests by checking if an error implements http.Handler. Use it instead of http.Error.

func endpoint(w http.ResponseWriter, r *http.Request) {
    httpsyproblem.Error(w, r, httpsyproblem.Wrap(http.StatusBadRequest, io.EOF))
}

Use the Details type directly if you need more control. The error value returns by Wrap is of type Details.

var err error = &httpsyproblem.Details{
    Detail: "This is not the Jedi that you are looking for",
    Instance: "/jedi/obi-wan",
    Status: http.StatusNotFound,
    Title: "Jedi Mind Trick",
}

Embed Details inside another type to add custom fields and use New to initialize it.

type MoreDetails struct {
    httpsyproblem.Details
    TraceID string `json:"trace_id,omitempty"`
}

var err error = &MoreDetails{
    Details: httpsyproblem.New(http.StatusBadRequest, io.EOF),
    TraceID: "42",
}

Read the rest of the documentation on pkg.go.dev. It's easy-peasy!

License

Package httpsyproblem is released under the terms of the ISC license.

Documentation

Overview

Package httpsyproblem provides a standard interface for handling API error responses in web applications. It implements RFC 7807 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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Error

func Error(w http.ResponseWriter, r *http.Request, err error)

Error replies to the request by calling err's handler if it implements http.Handler or with a plain text message otherwise.

Example (Json)
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"

	"github.com/askeladdk/httpsyproblem"
)

func main() {
	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/", nil)

	err := httpsyproblem.Wrap(http.StatusBadRequest, io.EOF)
	err.(*httpsyproblem.Details).Instance = "/products/42"
	httpsyproblem.Error(w, r, err)

	fmt.Println(w.Result().Status)
	_, _ = io.Copy(os.Stdout, w.Body)
}
Output:

400 Bad Request
{"detail":"EOF","instance":"/products/42","status":400,"title":"Bad Request","type":"about:blank"}
Example (Text)
package main

import (
	"fmt"
	"io"
	"net/http/httptest"
	"os"

	"github.com/askeladdk/httpsyproblem"
)

func main() {
	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/", nil)

	httpsyproblem.Error(w, r, io.EOF)

	fmt.Println(w.Result().Status)
	_, _ = io.Copy(os.Stdout, w.Body)
}
Output:

500 Internal Server Error
EOF

func StatusCode

func StatusCode(err error) int

StatusCode reports the HTTP status code associated with err if it implements the StatusCode() int method, 500 Internal Server Error otherwise, or 200 OK if err is nil.

Example
package main

import (
	"fmt"
	"io"
	"net/http"

	"github.com/askeladdk/httpsyproblem"
)

func main() {
	fmt.Println(httpsyproblem.StatusCode(nil))
	fmt.Println(httpsyproblem.StatusCode(io.EOF))
	fmt.Println(httpsyproblem.StatusCode(httpsyproblem.Wrap(http.StatusBadRequest, io.EOF)))
}
Output:

200
500
400

func Wrap

func Wrap(code int, err error) error

Wrap associates an error with a status code and wraps it in a Details. The Detail field is set to err.Error(). The Status and Title fields are set according to code or to StatusCode(err) if code is 0.

Example
package main

import (
	"fmt"
	"io"
	"net/http"

	"github.com/askeladdk/httpsyproblem"
)

func main() {
	// Errors are associated with 500 Internal Server Error by default.
	err := io.EOF
	fmt.Println(httpsyproblem.StatusCode(err), err)

	// Wrap any error to associate it with a status code.
	err = httpsyproblem.Wrap(http.StatusBadRequest, err)
	fmt.Println(httpsyproblem.StatusCode(err), err)

	// Wrapping an already wrapped error changes the status code but preserves the details.
	err = httpsyproblem.Wrap(http.StatusNotFound, err)
	fmt.Println(httpsyproblem.StatusCode(err), err)
	fmt.Println(err.(*httpsyproblem.Details).Detail)
}
Output:

500 EOF
400 Bad Request
404 Not Found
EOF

func Wrapf

func Wrapf(code int, format string, a ...interface{}) error

Wrapf is a shorthand for wrapping the result of fmt.Errorf.

Types

type Details

type Details 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
}

Details implements the RFC 7807 model and can marshaled to JSON and XML.

Additional fields can be added by embedding Details inside another struct:

type MoreDetails struct {
    httpsyproblem.Details
    TraceID string `json:"trace_id" xml:"trace_id"`
}

func New

func New(code int, err error) Details

New returns a Details with the Detail, Status and Title fields set according to code and err.

func (*Details) Error

func (details *Details) Error() string

Error implements the error interface and returns the Title field.

func (*Details) ServeHTTP

func (details *Details) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler and replies to the request with a problem details object in JSON or XML format depending on the Accept header header. Panics if an error occurred while marshaling.

func (*Details) StatusCode

func (details *Details) StatusCode() int

StatusCode implements the interface used by StatusCode.

func (*Details) Unwrap

func (details *Details) Unwrap() error

Unwrap implements the interface used by errors.Unwrap() and returns the wrapped error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL