signature

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Nov 14, 2024 License: BSD-3-Clause Imports: 5 Imported by: 0

README

This package is intended to reduce duplication of common steps in http handlers implemented as http.HandlerFunc. Those common steps are parsing an input from request, unmarshaling the result into http.ResponseWriter and handling errors that can occur in any of those steps or inside the handler logic itself.

It does this by allowing handlers to be defined with a new function signature, that can include an input type as parameter, a response type in return values, and always has error as last return value.

The handlers with enhanced signature can be than wrapped using function like signature.WrapHandler so it can be used as a http.HandlerFunc type

Example:

main() {
	w := signature.DefaultWrapper()

	r := chi.NewRouter()
	r.Get("/endpoint1", signature.WrapHandler(w, handleEndpoint1))
}

func handleEndpoint1(w http.ResponseWriter, r *http.Request, input MyInputStruct) (MyResponseStruct, error){
	// access the input variable of type MyInputStruct, do handler logic, return MyResponseStruct or error
	return theLogic(input)
}

Instead of using the repetetive http.HandlerFunc:

main() {
	r := chi.NewRouter()
	r.Get("/endpoint1", handleEndpoint1)
}

func handleEndpoint1(w http.ResponseWriter, r *http.Request) {
	if err := parseIntoMyStruct(&MyInputStruct{}); err != nil {
		writeError(w, err)
		return
	}
    
	// in this case, the actual logic is still just one line
	result, err := theLogic(input)
	if err != nil {
		writeError(w, err)
		return
	}
	
	if err := writeResult(result); err != nil {
		writeError(w, err)
		return
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInputGet is passed to ErrorHandlerFunc when WrapHandler (or derived) fails in the first step (parsing input)
	ErrInputGet = errors.New("parsing input")
	// ErrInnerHandler is passed to ErrorHandlerFunc when WrapHandler (or derived) fails in the second step (inner handler)
	ErrInnerHandler = errors.New("inner handler")
	// ErrResponseMarshal is passed to ErrorHandlerFunc when WrapHandler (or derived) fails in the third step (marshaling response object)
	ErrResponseMarshal = errors.New("marshaling response")
)

Functions

func AlwaysInternalErrorHandle

func AlwaysInternalErrorHandle(w http.ResponseWriter, _ *http.Request, _ error)

AlwaysInternalErrorHandle is a function usable as ErrorHandlerFunc. It writes 500 http status code on error. Error message not returned in response and is lost.

func DefaultResponseMarshal

func DefaultResponseMarshal(w http.ResponseWriter, _ *http.Request, src any) error

DefaultResponseMarshal is a ResponseMarshalerFunc that writes 200 OK http status code with JSON marshaled object. 204 No Content http status code is returned if no response object is provided (i.e. when using WrapHandlerInput or WrapHandlerError)

func InputGetErrorHandle

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

InputGetErrorHandle is a function usable as ErrorHandlerFunc. It writes a 400 Bad Request http status code to http.ResponseWriter if the error is from parsing input. Otherwise, writes 500 Internal Server Error http status code on error. In either case, error message is not returned in response and is lost

func UnmarshalRequestBody

func UnmarshalRequestBody(r *http.Request, target any) error

UnmarshalRequestBody decodes a body into a struct. This function expects the request body to be a JSON object and target to be a pointer to expected struct. If the request body is invalid, it returns an error.

func WrapHandler

func WrapHandler[TInput any, TResponse any](wrapper Wrapper, handler func(http.ResponseWriter, *http.Request, TInput) (TResponse, error)) http.HandlerFunc

WrapHandler enables a handler with signature of second parameter to be used as a http.HandlerFunc. 1. Before calling such inner handler, the http.request is used to get the input parameter of type TInput for the handler, using InputGetterFunc in Wrapper. 2. Then the inner handler is called with such created TInput. 3. If the handler succeeds (returns nil error), The first return value (of type TResponse) is passed to ResponseMarshalerFunc of Wrapper. If any of the above steps returns error, the ErrorHandlerFunc is called with that error.

func WrapHandlerError

func WrapHandlerError(wrapper Wrapper, handler func(http.ResponseWriter, *http.Request) error) http.HandlerFunc

WrapHandlerError enables a handler with signature of second parameter to be used as a http.HandlerFunc. See WrapHandler for general idea. Compared to WrapHandler, the first step is skipped (no parsed input for inner handler is provided), and in the last step, the ResponseMarshalerFunc receives http.NoBody as a response object (and as such, the ResponseMarshalerFunc should handle the http.NoBody value gracefully)

func WrapHandlerInput

func WrapHandlerInput[TInput any](wrapper Wrapper, handler func(http.ResponseWriter, *http.Request, TInput) error) http.HandlerFunc

WrapHandlerInput enables a handler with signature of second parameter to be used as a http.HandlerFunc. See WrapHandler for general idea. Compared to WrapHandler, in the last step, the ResponseMarshalerFunc receives http.NoBody as a response object (and as such, the ResponseMarshalerFunc should handle the http.NoBody value gracefully)

func WrapHandlerResponse

func WrapHandlerResponse[TResponse any](wrapper Wrapper, handler func(http.ResponseWriter, *http.Request) (TResponse, error)) http.HandlerFunc

WrapHandlerResponse enables a handler with signature of second parameter to be used as a http.HandlerFunc. See WrapHandler for general idea. Compared to WrapHandler, the first step is skipped (no parsed input for inner handler is provided)

Types

type ErrorHandlerFunc

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

ErrorHandlerFunc is a function that is used in WrapHandler and related functions if any of the steps fail. The passed err is wrapped in one of ErrInputGet, ErrInnerHandler or ErrResponseMarshal to distinguish the step that failed.

Note that if the error occurs on unmarshaling response with still valid http.ResponseWriter, and that step already wrote into the writer, the unmarshaled response (including e.g. http headers) may be inconsistent if error handler also writes.

type InputGetterFunc

type InputGetterFunc func(r *http.Request, dest any) error

InputGetterFunc is a function that is used in WrapHandler and WrapHandlerInput to parse request into declared input type. Before calling the inner handler, the InputGetterFunc is called to fill the struct that is then passed to the inner handler. If inner handler does not declare an input type (i.e. WrapHandlerResponse and WrapHandlerError), this function is not called at all.

type ResponseMarshalerFunc

type ResponseMarshalerFunc func(w http.ResponseWriter, r *http.Request, src any) error

ResponseMarshalerFunc is a function that is used in WrapHandler and related functions to marshal declared response type. After the inner handler succeeds, the ResponseMarshalerFunc receives http.ResponseWriter and http.Request of handled request, and a type that an inner handler function declared as its first (non-error) return value. If the inner handler does not declare such return value (i.e. for WrapHandlerInput and WrapHandlerError), the ResponseMarshalerFunc receives http.NoBody as the src parameter.

func FixedResponseCodeMarshal

func FixedResponseCodeMarshal(statusCode int) ResponseMarshalerFunc

FixedResponseCodeMarshal returns a ResponseMarshalerFunc that always writes provided http status code on success.

type Wrapper

type Wrapper struct {
	// contains filtered or unexported fields
}

Wrapper needs to be passed to WrapHandler and related functions. It contains the common handling of parsing http.Request to needed type, marshaling the response of needed type, and handling the errors that occur in any of those steps or in the inner handler (with modified signature)

func DefaultWrapper

func DefaultWrapper() Wrapper

DefaultWrapper Creates a Wrapper with default functions for each needed step.

Input is parsed only from http.Request body, using JSON unmarshal. A custom InputGetterFunc is needed to parse also the query and path parameters, but param package can be used to do most.

Response is marshaled using a WriteResponse wrapper in parent package, which uses JSON marshal.

Error handler also uses a WriteErrorResponse of parent package. It is recommended to replace this to implement any custom error handling (matching any application errors). Default handler only returns http code 400 on unmarshal error and 500 otherwise.

func (Wrapper) WithErrorHandler

func (w Wrapper) WithErrorHandler(f ErrorHandlerFunc) Wrapper

WithErrorHandler returns a copy of Wrapper with new ErrorHandlerFunc

func (Wrapper) WithInputGetter

func (w Wrapper) WithInputGetter(f InputGetterFunc) Wrapper

WithInputGetter returns a copy of Wrapper with new InputGetterFunc

func (Wrapper) WithResponseMarshaler

func (w Wrapper) WithResponseMarshaler(f ResponseMarshalerFunc) Wrapper

WithResponseMarshaler returns a copy of Wrapper with new ResponseMarshalerFunc

Jump to

Keyboard shortcuts

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