httperror

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2022 License: MIT Imports: 8 Imported by: 1

README

Package httperror is for writing HTTP handlers that return errors instead of handling them directly.

  • installation: go get github.com/johnwarden/httperror
  • godoc

Overview

func helloHandler(w http.ResponseWriter, r *http.Request) error {

	w.Header().Set("Content-Type", "text/plain")

	name, ok := r.URL.Query()["name"];
	if !ok {
		return httperror.New(http.StatusBadRequest, "missing 'name' parameter")
	}

	fmt.Fprintf(w, "Hello, %s\n", name[0])

	return nil;
}

func main() {

	h := httperror.HandlerFunc(helloHandler)

	http.Handle("/hello", h)

	http.ListenAndServe(":8080", nil)
}

Unlike a standard http.HandlerFunc, the helloHandler function above can return an error. Although there is no explicit error handling code in this example, if you run it and fetch http://localhost:8080/hello without a name URL parameter, an appropriate plain-text 400 Bad Request page will be served.

This is because helloHandler is converted into a httperror.HandlerFunc, which implements the standard http.Handler interface, but the 400 Bad Raquest error returned by helloHandler will be handled by a default error handler that serves an appropriate error page.

Advantages to Returning Errors over Handling Them Directly

  • more idiomatic Go
  • reduce risk of "naked returns" as described by Preslav Rachev's in I Don't Like Go's Default HTTP Handlers
  • middleware can inspect errors, extract status codes, add context, and appropriately log and handle errors

This package is built based on the philosophy that HTTP frameworks are not needed in Go: the net/http package, and the various router, middleware, and templating libraries that are compatible with it, are sufficient. However, the lack of an error return value in the signature of standard http handler functions is perhaps a small design flaw in the http package. This package addresses this without tying you to a framework: httperror.Handler is an http.Handler. You can apply standard http Handler middleware to it. And your handler functions look exactly as they would look if net/http had been designed differently.

Custom Error Handlers

Use WrapHandlerFunc to add a custom error handler.

func customErrorHandler(w http.ResponseWriter, e error) { 
	s := httperror.StatusCode(e)
	w.WriteHeader(s)
	// now serve an appropriate error response
}

h := httperror.WrapHandlerFunc(helloHandler, customErrorHandler)

Here is a more complete example.

Middleware

Returning errors from functions enable some new middleware patterns.

func myMiddleware(h httperror.Handler) httperror.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) error {
		err := h.Serve(w,r)
		if err != nil {
			// do something with the error!
		}
		// return nil if the error has been handled.
		return err
	}
}

Here is an example of custom middleware that logs errors.

PanicMiddleware and XPanicMiddleware are simple middleware functions that convert panics to errors. This ensures users are served an appropriate 500 error response on panic instead of an empty response. And it allows middleware to appropriately inspects, count, and log panics as they do other errors.

Extracting, Embedding, and Comparing HTTP Status Codes

// Pre-Defined Errors
e := httperror.NotFound

// Extracting Status
httperror.StatusCode(e) // 404

// Constructing Errors
e = httperror.New(http.StatusNotFound, "no such product ID")

// Comparing Errors
errors.Is(e, httperror.NotFound) // true

// Wrapping Errors
var ErrNoSuchProductID = fmt.Errorf("no such product ID")
e = httperror.Wrap(ErrNoSuchProductID, http.StatusNotFound)

// Comparing Wrapped Errors
errors.Is(e, ErrNoSuchProductID) // true
errors.Is(e, httperror.NotFound) // also true!

Public Error Messages

The default error handler, DefaultErrorHandler will not show the full error string to users, because these often contain stack traces or other implementation details that should not be exposed to the public.

But if the error value has an embedded public error message, the error handler will display this to the user. To embed a public error message, create an error using NewPublic or PublicErrorf instead of New or Errorf:

e := httperror.NewPublic(404, "Sorry, we can't find a product with this ID")

Public error messages are extracted by PublicMessage:

m := httperror.PublicMessage(e)

If your custom error type defines a PublicMessage() string method, then PublicMessage will call and return the value from that method.

Generic Handler and HandlerFunc Types

This package defines generic versions of httperror.Handler and httperror.HandlerFunc that accept a third parameter of any type. These are httperror.XHandler and httperror.XHandlerFunc.

The third parameter can contain parsed request parameters, authorized user IDs, and other information required by handlers. For example, the helloHandler function in the introductory example might be cleaner if it accepted its parameters as a struct.

type HelloParams struct {
	Name string
}

func helloHandler(w http.ResponseWriter, r *http.Request, ps HelloParams) error { 
	fmt.Fprintf(w, "Hello, %s\n", ps.Name)
	return nil
}

h = httperror.XHandlerFunc[HelloParams](helloHandler)

Use with Other Routers, Frameworks, and Middleware

Many routers and frameworks use a custom type for passing parsed request parameters or a request context. A generic httperror.XHandler can accept a third argument of any type, so you can write handlers that work with your preferred framework but that also return errors. For example:

var ginHandler httperror.XHandler[*gin.Context] = func(w http.ResponseWriter, r *http.Request, c *gin.Context) error { ... }
var httprouterHandler httperror.XHandler[httprouter.Params] = func(w http.ResponseWriter, r *http.Request, p httprouter.Params) error  { ... }

See this example of using this package pattern with a github.com/julienschmidt/httprouter.

One advantages of writing functions this way, other than that they can return errors instead of handling them, is that you can apply generic middleware written for httperror.XHandlers, such as PanicMiddleware for converting panics to errors. In fact, this package makes it easy to apply middleware that was not written for any particular router or framework.

Applying Standard Middleware

You can apply middleware written for standard HTTP handlers to an httperror.Handler or an httperror.XHandler, because they both implement the http.Handler interface. See the standard middleware example.

However, the handler returned from a standard middleware wrapper will be an http.Handler, and will therefore not be able to return an error or accept additional parameters. Instead, use ApplyStandardMiddleware and XApplyStandardMiddleware, which return an httperror.Handler or an httperror.XHandler respectively. You can see an example of this in the httprouter example.

Similar Packages

github.com/caarlos0/httperr uses a very similar approach, for example the definition of: httperr.HandlerFunc and httperror.HandlerFunc are identical. I have this package to be mostly compatible with this httperr.

Example: Custom Error Handler

This example extends the basic example from the introduction by adding a custom error handler.

package httperror_test

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"

	"github.com/johnwarden/httperror"
)

func Example_customErrorHandler() {
	// This is the same helloHandler as the introduction. Add a custom error handler.
	h := httperror.WrapHandlerFunc(helloHandler, customErrorHandler)

	_, o := testRequest(h, "/hello")
	fmt.Println(o)
	// Output: 400 Sorry, we couldn't parse your request: missing 'name' parameter
}

func helloHandler(w http.ResponseWriter, r *http.Request) error {
	w.Header().Set("Content-Type", "text/plain")

	name, ok := r.URL.Query()["name"]
	if !ok {
		return httperror.NewPublic(http.StatusBadRequest, "missing 'name' parameter")
	}

	fmt.Fprintf(w, "Hello, %s\n", name[0])

	return nil
}

func customErrorHandler(w http.ResponseWriter, err error) {

	s := httperror.StatusCode(err)
	w.WriteHeader(s)

	if errors.Is(err, httperror.BadRequest) {
		// Handle 400 Bad Request errors by showing a user-friendly message.

		var m bytes.Buffer
		m.Write([]byte("Sorry, we couldn't parse your request: "))
		m.Write([]byte(httperror.PublicMessage(err)))

		httperror.WriteResponse(w, httperror.StatusCode(err), m.Bytes())

	} else {
		// Else use the default error handler.
		httperror.DefaultErrorHandler(w, err)
	}
}

Example: Log Middleware

The following example extends the basic example from the introduction by adding custom logging middleware. Actual logging middleware would probably need to be much more complex to correctly capture information from the response such as the status code for successful requests.

package httperror_test

import (
	"fmt"
	"net/http"

	"github.com/johnwarden/httperror"
)


func Example_logMiddleware() {
	// This is the same helloHandler as the introduction
	h := httperror.HandlerFunc(helloHandler)

	// But add some custom middleware to handle and log errors.
	h = customLogMiddleware(h)

	_, o := testRequest(h, "/hello")
	fmt.Println(o)
	// Output: HTTP Handler returned error 400 Bad Request: missing 'name' parameter
	// 400 Sorry, we couldn't parse your request: missing 'name' parameter
}

func customLogMiddleware(h httperror.Handler) httperror.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) error {

		// TODO: custom pre-request actions such as wrapping the response writer.

		err := h.Serve(w, r)

		if err != nil {
			// TODO: insert your application's error logging code here.
			fmt.Printf("HTTP Handler returned error %s\n", err)
		}

		return err
	}
}

Example: Standard Middleware

Because httperror.Handler implements the standard http.Handler interface, you can apply any of the many middleware created by the Go community for standard http Handlers, as illustrated in the example below.

Note however, the resulting handlers after wrapping will be http.Handlers, and will therefore not be able to return an error or accept additional parameters. The httprouter example shows hows to use ApplyStandardMiddleware to apply standard middleware to httperror.Handlers without changing their signature.

package httperror_test

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

	"github.com/johnwarden/httperror"
	gorilla "github.com/gorilla/handlers"
)

func Example_applyMiddleware() {
	// This is the same helloHandler as the introduction.
	h := httperror.HandlerFunc(helloHandler)

	// Apply some middleware
	sh := gziphandler.GzipHandler(helloHandler)
	sh := gorilla.LoggingHandler(os.Stdout, h)

	_, o := testRequest(sh, "/hello?name=Beautiful")
	fmt.Println(o)
	// Outputs a log line plus
	// Hello, Beautiful
}

Example: HTTPRouter

This example illustrates the use of the error-returning paradigm described in this document with a popular router package, github.com/julienschmidt/httprouter. To make things more interesting, the handler function accepts its parameters as a struct instead of a value of type httprouter.Params, thereby decoupling the handler from the router.

Further, we illustrate the use of ApplyStandardMiddleware to wrap our handler with middleware written for a standard http.Handler, but still allow our third parameter to be passed in by the router.

import (
	"fmt"
	"net/http"

	"github.com/johnwarden/httperror"
	"github.com/julienschmidt/httprouter"
	"github.com/NYTimes/gziphandler"
)

func Example_httprouter() {
	router := httprouter.New()

	// first, apply middleware that parses request params and
	// converts our handler into an httprouter.Handle
	h := requestParserMiddleware(helloRouterHandler)

	// next, apply some middleware. We still have an httprouter.Handle
	h := httperror.ApplyStandardMiddleware[HelloParams](h, gziphandler.GzipHandler)

	router.GET("/hello/:name", h)

	_, o := testRequest(router, "/hello/Sunshine")
	fmt.Println(o)
	// Output: Hello, Sunshine
}

type HelloParams struct {
	Name string
}

// This helloRouterHandler func looks like the standard http Handler, but it takes
// a third argument of type HelloParams argument and can return an error.

func helloRouterHandler(w http.ResponseWriter, r *http.Request, ps HelloParams) error { 
	if ps.Name == "" { 
		return httperror.NewPublic(http.StatusBadRequest, "missing 'name' parameter") 
	}

	fmt.Fprintf(w, "Hello, %s\n", ps.Name)

	return nil
}

// requestParserMiddleware wraps a handler function of type httperror.XHandler[HelloParams]
// and converts it into a httprouter.Handle. The resulting function
// converts its argument of type httprouter.Params into a value of type HelloParams,
// and passes it to the inner handler function. 

func requestParserMiddleware(h httperror.XHandler[HelloParams]) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

		var params HelloParams
		params.Name = ps.ByName("name")

		err := h(w, r, params)
		if err != nil {
			httperror.DefaultErrorHandler(w, err)
		}
	}
}

Example: Panic Middleware

This example shows how to use [httperror.PanicMiddleware] (https://pkg.go.dev/github.com/johnwarden/httperror#PanicMiddleware) to serve an appropriate error page to the user on panic and then trigger a clean HTTP server shutdown.

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/johnwarden/httperror"
)


const maxShutDownTimeout = 5 * time.Second

func main() {
	httpServer := &http.Server{
		Addr: ":8080",
	}

	shutdown := func() {
		// shut down the HTTP server with a timeout in case the server doesn't want to shut down.
		// or waiting for connections to change to idle status takes too long.
		ctxWithTimeout, cancel := context.WithTimeout(context.Background(), maxShutDownTimeout)
		defer cancel()
		err := httpServer.Shutdown(ctxWithTimeout)
		if err != nil {
			// if server doesn't respond to shutdown signal, nothing remains but to panic.
			log.Panic(err)
		}
	}

	errorHandler := func(w http.ResponseWriter, err error) {
		if errors.Is(err, httperror.Panic) {
			// the shutdown function must be called in a goroutine. Otherwise, if it is used
			// to shutdown the server, we'll get a deadlock with the server shutdown function
			// waiting for this request handler to finish, and this request waiting for the
			// server shutdown function.
			fmt.Println("Shutting down")
			go shutdown()
		}

		// Now make sure we serve the user an appropriate error page.
		httperror.DefaultErrorHandler(w, err)
	}

	// This middleware converts panics to errors.
	h := httperror.PanicMiddleware(getMeOuttaHere)

	// This is the same helloHandler as the introduction. Add a custom error handler.
	httpServer.Handler = httperror.WrapHandlerFunc(h, errorHandler)

	err := httpServer.ListenAndServe()
}

var getMeOuttaHere = httperror.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
	w.Header().Set("Content-Type", "text/plain")
	panic("Get me outta here!")
	return nil
})

Documentation

Overview

Package httperror is for writing HTTP handlers that return errors instead of handling them directly. See the documentation at https://github.com/johnwarden/httperror

Index

Constants

This section is empty.

Variables

View Source
var BadGateway = httpError{http.StatusBadGateway}

BadGateway represents the StatusBadGateway HTTP error.

View Source
var BadRequest = httpError{http.StatusBadRequest}

BadRequest represents the StatusBadRequest HTTP error.

View Source
var Conflict = httpError{http.StatusConflict}

Conflict represents the StatusConflict HTTP error.

View Source
var ExpectationFailed = httpError{http.StatusExpectationFailed}

ExpectationFailed represents the StatusExpectationFailed HTTP error.

View Source
var FailedDependency = httpError{http.StatusFailedDependency}

FailedDependency represents the StatusFailedDependency HTTP error.

View Source
var Forbidden = httpError{http.StatusForbidden}

Forbidden represents the StatusForbidden HTTP error.

View Source
var GatewayTimeout = httpError{http.StatusGatewayTimeout}

GatewayTimeout represents the StatusGatewayTimeout HTTP error.

View Source
var Gone = httpError{http.StatusGone}

Gone represents the StatusGone HTTP error.

View Source
var HTTPVersionNotSupported = httpError{http.StatusHTTPVersionNotSupported}

HTTPVersionNotSupported represents the StatusHTTPVersionNotSupported HTTP error.

View Source
var InsufficientStorage = httpError{http.StatusInsufficientStorage}

InsufficientStorage represents the StatusInsufficientStorage HTTP error.

View Source
var InternalServerError = httpError{http.StatusInternalServerError}

InternalServerError represents the StatusInternalServerError HTTP error.

View Source
var LengthRequired = httpError{http.StatusLengthRequired}

LengthRequired represents the StatusLengthRequired HTTP error.

View Source
var Locked = httpError{http.StatusLocked}

Locked represents the StatusLocked HTTP error.

View Source
var LoopDetected = httpError{http.StatusLoopDetected}

LoopDetected represents the StatusLoopDetected HTTP error.

View Source
var MethodNotAllowed = httpError{http.StatusMethodNotAllowed}

MethodNotAllowed represents the StatusMethodNotAllowed HTTP error.

View Source
var MisdirectedRequest = httpError{http.StatusMisdirectedRequest}

MisdirectedRequest represents the StatusMisdirectedRequest HTTP error.

View Source
var NetworkAuthenticationRequired = httpError{http.StatusNetworkAuthenticationRequired}

NetworkAuthenticationRequired represents the StatusNetworkAuthenticationRequired HTTP error.

View Source
var NotAcceptable = httpError{http.StatusNotAcceptable}

NotAcceptable represents the StatusNotAcceptable HTTP error.

View Source
var NotExtended = httpError{http.StatusNotExtended}

NotExtended represents the StatusNotExtended HTTP error.

View Source
var NotFound = httpError{http.StatusNotFound}

NotFound represents the StatusNotFound HTTP error.

View Source
var NotImplemented = httpError{http.StatusNotImplemented}

NotImplemented represents the StatusNotImplemented HTTP error.

View Source
var Panic = panicError{}
View Source
var PaymentRequired = httpError{http.StatusPaymentRequired}

PaymentRequired represents the StatusPaymentRequired HTTP error.

View Source
var PreconditionFailed = httpError{http.StatusPreconditionFailed}

PreconditionFailed represents the StatusPreconditionFailed HTTP error.

View Source
var PreconditionRequired = httpError{http.StatusPreconditionRequired}

PreconditionRequired represents the StatusPreconditionRequired HTTP error.

View Source
var ProxyAuthRequired = httpError{http.StatusProxyAuthRequired}

ProxyAuthRequired represents the StatusProxyAuthRequired HTTP error.

View Source
var RequestEntityTooLarge = httpError{http.StatusRequestEntityTooLarge}

RequestEntityTooLarge represents the StatusRequestEntityTooLarge HTTP error.

View Source
var RequestHeaderFieldsTooLarge = httpError{http.StatusRequestHeaderFieldsTooLarge}

RequestHeaderFieldsTooLarge represents the StatusRequestHeaderFieldsTooLarge HTTP error.

View Source
var RequestTimeout = httpError{http.StatusRequestTimeout}

RequestTimeout represents the StatusRequestTimeout HTTP error.

View Source
var RequestURITooLong = httpError{http.StatusRequestURITooLong}

RequestURITooLong represents the StatusRequestURITooLong HTTP error.

View Source
var RequestedRangeNotSatisfiable = httpError{http.StatusRequestedRangeNotSatisfiable}

RequestedRangeNotSatisfiable represents the StatusRequestedRangeNotSatisfiable HTTP error.

View Source
var ServiceUnavailable = httpError{http.StatusServiceUnavailable}

ServiceUnavailable represents the StatusServiceUnavailable HTTP error.

View Source
var Teapot = httpError{http.StatusTeapot}

Teapot represents the StatusTeapot HTTP error.

View Source
var TooEarly = httpError{http.StatusTooEarly}

TooEarly represents the StatusTooEarly HTTP error.

View Source
var TooManyRequests = httpError{http.StatusTooManyRequests}

TooManyRequests represents the StatusTooManyRequests HTTP error.

View Source
var Unauthorized = httpError{http.StatusUnauthorized}

Unauthorized represents the StatusUnauthorized HTTP error.

View Source
var UnavailableForLegalReasons = httpError{http.StatusUnavailableForLegalReasons}

UnavailableForLegalReasons represents the StatusUnavailableForLegalReasons HTTP error.

View Source
var UnprocessableEntity = httpError{http.StatusUnprocessableEntity}

UnprocessableEntity represents the StatusUnprocessableEntity HTTP error.

View Source
var UnsupportedMediaType = httpError{http.StatusUnsupportedMediaType}

UnsupportedMediaType represents the StatusUnsupportedMediaType HTTP error.

View Source
var UpgradeRequired = httpError{http.StatusUpgradeRequired}

UpgradeRequired represents the StatusUpgradeRequired HTTP error.

View Source
var VariantAlsoNegotiates = httpError{http.StatusVariantAlsoNegotiates}

VariantAlsoNegotiates represents the StatusVariantAlsoNegotiates HTTP error.

Functions

func DefaultErrorHandler

func DefaultErrorHandler(w http.ResponseWriter, e error)

DefaultErrorHandler writes a reasonable default error response, using the status code from the error if it can be extracted (see StatusCode), or 500 by default, using the content type from from w.Header(), or text/html by default, and using any public message (see PublicErrorf and Public.)

func Errorf

func Errorf(s int, format string, args ...interface{}) error

Errorf works like fmt.Errorf but it also embeds an HTTP status code. The status code can be extracted using httperror.StatusCode.

func New

func New(s int, m string) error

New constructs an error with an embedded an HTTP status code. The status code can be extracted using httperror.StatusCode.

func NewPublic

func NewPublic(status int, message string) error

NewPublic returns a new public error with the given status code and public error message generated using the format string and arguments. The resulting error value implements the the httperror.Public interface.

func PublicErrorf

func PublicErrorf(status int, format string, args ...interface{}) error

func PublicMessage

func PublicMessage(err error) string

PublicMessage extracts the public message from errors that have a `PUblicMessage() string` method.

func StatusCode

func StatusCode(err error) int

StatusCode extracts the HTTP status code from an error created by this package. If the error doesn't have an embedded status code, it returns InternalServerError. If the error is nil, returns 200 OK.

func Wrap

func Wrap(err error, status int) error

Wrap wraps an error and embeds an HTTP status code that can be extracted using httperror.StatusCode

func WrapHandlerFunc

func WrapHandlerFunc(h func(w http.ResponseWriter, r *http.Request) error, eh ErrorHandler) http.HandlerFunc

WrapHandlerFunc wraps an HandlerFunc function with a custom error handler. Return a standard http.HandlerFunc since returning an error is irrelevant once it has been handled.

func WrapXHandlerFunc

func WrapXHandlerFunc[P any](h func(w http.ResponseWriter, r *http.Request, p P) error, eh ErrorHandler) func(w http.ResponseWriter, r *http.Request, p P)

WrapXHandlerFunc constructs an httperror.XHandlerFunc with a custom error handler. Returns an function with the same signature but without the error return value.

func WriteResponse

func WriteResponse(w http.ResponseWriter, s int, m []byte)

WriteResponse writes a reasonable default error response given the status code and optional error message. The default error handler DefaultErrorHandler calls this method after extracting the status code and any public error message.

Types

type ErrorHandler

type ErrorHandler = func(http.ResponseWriter, error)

ErrorHandler handles an error.

type Handler

type Handler interface {
	ServeHTTP(w http.ResponseWriter, r *http.Request)
	Serve(w http.ResponseWriter, r *http.Request) error
}

Handler is like the standard http.Handler interface type, but it also implements the Serve method which returns an error. When used as a standard http.Handler, any errors will be handled by the default error handler DefaultErrorHandler. But code that understands the httperror.Handler interface and can deal with returned errors can call the Serve method.

type HandlerFunc

type HandlerFunc func(w http.ResponseWriter, r *http.Request) error

HandlerFunc is like the standard http.HandlerFunc type, but it returns an error. HandlerFunc implements both the httperror.Handler and the http.Handler interface.

func ApplyStandardMiddleware added in v1.2.0

func ApplyStandardMiddleware(h Handler, ms ...StandardMiddleware) HandlerFunc

ApplyStandardMiddleware applies middleware written for a standard http.Handler to an httperror.Handler, returning an httperror.Handler. It is possible to apply standard middleware to httperror.Handler without using this function,because httperror.Handler implements the standard http.Handler interface. However, the result would be an http.Handler, not an httperror.Handler, and so parameters could not passed to it and it could not return an error. This function solves that problem by passing errors and parameters through the context.

func PanicMiddleware added in v1.1.0

func PanicMiddleware(h Handler) HandlerFunc

PanicMiddleware wraps a httperror.Handler, returning a new httperror.HandlerFunc that recovers from panics and returns them as errors. Panic error can be identified using errors.Is(err, httperror.Panic)

func (HandlerFunc) Serve

Serve makes httperror.HandlerFunc implement the httperror.Handler interface

func (HandlerFunc) ServeHTTP

func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP makes httperror.HandlerFunc implement the standard http.Handler interface. Any errors will be handled by the default error handler DefaultErrorHandler.

type Public

type Public = interface {
	PublicMessage() string
}

Public is an interface that requires a PublicMessage() string method. httperror.PublicMessage will extract the public error message from errors that implements this interface.

type StandardMiddleware added in v1.2.0

type StandardMiddleware = func(http.Handler) http.Handler

StandardMiddleware is a standard http.Handler wrapper.

type XHandler

type XHandler[P any] interface {
	ServeHTTP(w http.ResponseWriter, r *http.Request)
	Serve(w http.ResponseWriter, r *http.Request, p P) error
}

XHandler is a generic version of httperror.Handler. The Serve method which accepts a third generic parameter.

type XHandlerFunc

type XHandlerFunc[P any] func(w http.ResponseWriter, r *http.Request, p P) error

XHandlerFunc is a generic version of httperror.HandlerFunc, that accepts a third generic parameter.

func XApplyStandardMiddleware added in v1.2.0

func XApplyStandardMiddleware[P any](h XHandler[P], ms ...StandardMiddleware) XHandlerFunc[P]

XApplyStandardMiddleware applies middleware written for a standard http.Handler to an httperror.XHandler, returning an httperror.XHandler. It is possible to apply standard middleware to httperror.XHandler without using this function,because httperror.XHandler implements the standard http.Handler interface. However, the result would be an http.Handler, not an httperror.XHandler, and so parameters could not passed to it and it could not return an error. This function solves that problem by passing errors and parameters through the context.

func XPanicMiddleware added in v1.1.0

func XPanicMiddleware[P any](h XHandler[P]) XHandlerFunc[P]

XPanicMiddleware wraps a httperror.XHandler, returning a new httperror.XHandlerFunc that recovers from panics and returns them as errors. Panic error can be identified using errors.Is(err, httperror.Panic)

func (XHandlerFunc[P]) Serve

func (h XHandlerFunc[P]) Serve(w http.ResponseWriter, r *http.Request, p P) error

Serve makes httperror.XHandlerFunc implement the httperror.Handler interface

func (XHandlerFunc[P]) ServeHTTP

func (h XHandlerFunc[P]) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP makes httperror.XHandlerFunc implement the standard http.Handler interface. Any errors will be handled by the default error handler DefaultErrorHandler.

Jump to

Keyboard shortcuts

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