glhf

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2023 License: MIT Imports: 8 Imported by: 1

README

GLHF

Generic Light-weight Handler Framework

Experimental

This is a experimental library. The goal is to evaluate leveraging generics to reduce duplicate code around deserializing and serializing http requests/responses.

GLHF is used in various production apps but caution should be used. We will be continually modifying GLHF.

Installation

GLHF is versioned using go modules. To install it, run

go get -u github.com/VauntDev/glhf

Use

GLHF is a simple library that abstracts common http patterns while aiming to not prevent more complex use cases.

Options

GLHF uses an options pattern. Options can be passed directly into the Http Method functions.

i.e glhf.Get(myhandler, WithDefaultContentType("application/proto"))

  • WithDefaultContentType: set the default contentType that should be used.
  • WithVerbose: enables verbose error responses, useful for developers that are running into error reading/writing http objects.

More options will be added over time, check the godocs for future options.

Marshaling

Request and Response marshaling is handled in glfh by utilizing the following HTTP headers

  • Content-Type: GLHF Content-type to determine how to marshal and unmarshal the request and response.
  • Accept : GLHF uses the request Accept header to determine what Content-Type should be used by the response.

Currenly glhf only support Application/json and Application/proto. The default is Application/json.

HTTP Routers

GLHF works with any http router that uses http.handlerFunc functions.

Standard Library HTTP Mux


mux := http.NewServeMux()
mux.HandleFunc("/todo", glhf.Post(h.CreateTodo))
mux.HandleFunc("/todo/{id}", glhf.Get(h.LookupTodo))

Gorilla mux


mux := mux.NewRouter()
mux.HandleFunc("/todo", glhf.Post(h.CreateTodo))
mux.HandleFunc("/todo/{id}", glhf.Get(h.LookupTodo))

Future Work

Examples

A sample application can be found in the example directory.

The following is an example GET handler. The functions expects an empty body and Todo body in response.


func (h *Handlers) LookupTodo(r *glhf.Request[glhf.EmptyBody], w *glhf.Response[pb.Todo]) {
    p := mux.Vars(r.HTTPRequest())

    id, ok := p["id"]
    if !ok {
        w.SetStatus(http.StatusInternalServerError)
        return
    }

    todo, err := h.service.Get(id)
    if err != nil {
        w.SetStatus(http.StatusNotFound)
        return
    }

    w.Body = todo
    log.Println("external handler", w.Body)
    w.SetStatus(http.StatusOK)
    return

}

The following is an example POST handler. The function expects a Todo body and Todo response.


func (h *Handlers) CreateTodo(r *glhf.Request[pb.Todo], w *glhf.Response[glhf.EmptyBody]) {
    t, err := r.Body()
    if err != nil {
        w.SetStatus(http.StatusBadRequest)
        return
    }

    if err := h.service.Add(t); err != nil {
        w.SetStatus(http.StatusInternalServerError)
        return
    }
    w.SetStatus(http.StatusOK)
    return
}

Documentation

Index

Constants

View Source
const (
	// ContentType header constant.
	ContentType = "Content-Type"
	Accept      = "Accept"

	// ContentJSON header value for JSON data.
	ContentJSON = "application/json"
	// ContentProto header value for proto buff
	ContentProto = "application/proto"

	// TODO :: Add additional content type support
	// ContentBinary header value for binary data.
	ContentBinary = "application/octet-stream"
	// ContentHTML header value for HTML data.
	ContentHTML = "text/html"
	// ContentText header value for Text data.
	ContentText = "text/plain"
	// ContentXHTML header value for XHTML data.
	ContentXHTML = "application/xhtml+xml"
	// ContentXML header value for XML data.
	ContentXML = "text/xml"
)

Variables

View Source
var (
	ErrProto                   = errors.New("value can not be used as proto message, invalid type")
	ErrUnsupportedResponseType = errors.New("response type unsupported")
	ErrUnsupportedRequestType  = errors.New("request type unsupported")
)

Functions

func Delete

func Delete[I Body, O Body](fn HandleFunc[I, O], options ...Options) http.HandlerFunc

Delete deletes the specified resource. The underlying request body is optional.

func Get

func Get[I EmptyBody, O any](fn HandleFunc[I, O], options ...Options) http.HandlerFunc

Get requests a representation of the specified resource. Expects an empty request body. If a request body is set, it will be ignored.

func Patch

func Patch[I Body, O Body](fn HandleFunc[I, O], options ...Options) http.HandlerFunc

Patch method is used to apply partial modifications to a resource. Required Request Body

func Post

func Post[I Body, O Body](fn HandleFunc[I, O], options ...Options) http.HandlerFunc

Post method can be used in two different ways, create a resource or perform and operation:. Optional request body

func Put

func Put[I Body, O Body](fn HandleFunc[I, O], options ...Options) http.HandlerFunc

Put method is used to replace a resource with a similar resource that includes a different set of values. Requires request body

Types

type Body

type Body any

Body is the request's body.

type EmptyBody

type EmptyBody struct{}

EmptyBody represents an empty http request body

type HandleFunc

type HandleFunc[I Body, O Body] func(*Request[I], *Response[O])

HandleFunc responds to an HTTP request. I and O represent the request body or response body.

type MarshalFunc added in v0.0.3

type MarshalFunc[I Body] func(I) ([]byte, error)

MarshalFunc defines how a body should be marshaled into bytes

type Options

type Options interface {
	Apply(*opts)
}

func WithDefaultContentType added in v0.0.3

func WithDefaultContentType(contentType string) Options

Defines the Default Content-type to be used if one is not set by the user.

func WithVerbose added in v0.0.3

func WithVerbose(b bool) Options

type Request

type Request[T Body] struct {
	// contains filtered or unexported fields
}

A Request represents an HTTP request received by a server

func (*Request[T]) Body

func (req *Request[T]) Body() *T

Body returns the generic http body, or nil if no body exists.

func (*Request[T]) Context

func (req *Request[T]) Context() context.Context

Context returns the underlying request's context.

func (*Request[T]) Cookie

func (req *Request[T]) Cookie(name string) (*http.Cookie, error)

Cookie warps the underlying request's cookie and returns the named cookie provided in the request or ErrNoCookie if not found. If multiple cookies match the given name, only one cookie will be returned.

func (*Request[T]) Cookies

func (req *Request[T]) Cookies() []*http.Cookie

Cookies wraps the underlying request and returns the HTTP cookies sent with the request.

func (*Request[T]) FormFile

func (req *Request[T]) FormFile(key string) (multipart.File, *multipart.FileHeader, error)

FormFile wraps the underlying request and returns the first file for the provided form key. FormFile calls ParseMultipartForm and ParseForm if necessary.

func (*Request[T]) FormValue

func (req *Request[T]) FormValue(key string) string

FormValue wraps the underlying request and returns the first value for the named component of the query. POST and PUT body parameters take precedence over URL query string values. FormValue calls ParseMultipartForm and ParseForm if necessary and ignores any errors returned by these functions. If key is not present, FormValue returns the empty string. To access multiple values of the same key, call ParseForm and then inspect Request.Form directly.

func (*Request[T]) HTTPRequest

func (req *Request[T]) HTTPRequest() *http.Request

HTTPRequest returns the raw HTTP Request. If the request contains a body, it will be nil and only available through the glhf request.Body() method. This method is a temporary function and will be deprecated in future releases.

func (*Request[T]) Header

func (req *Request[T]) Header() http.Header

Header wraps http.Request.Header

func (*Request[T]) MultipartReader

func (req *Request[T]) MultipartReader() (*multipart.Reader, error)

MultipartReader wraps the underlying request and returns a MIME multipart reader if this is a multipart/form-data or a multipart/mixed POST request, else returns nil and an error. Use this function instead of ParseMultipartForm to process the request body as a stream.

func (*Request[T]) ParseForm

func (req *Request[T]) ParseForm() error

ParseForm wraps the underlying request and populates r.Form and r.PostForm.

func (*Request[T]) ParseMultipartForm

func (req *Request[T]) ParseMultipartForm(maxMemory int64) error

ParseMultipartForm wraps the underlying request and parses a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files. ParseMultipartForm calls ParseForm if necessary. If ParseForm returns an error, ParseMultipartForm returns it but also continues parsing the request body. After one call to ParseMultipartForm, subsequent calls have no effect.

func (*Request[T]) PostFormValue

func (req *Request[T]) PostFormValue(key string) string

PostFormValue wraps the underlying request and returns the first value for the named component of the POST, PATCH, or PUT request body. URL query parameters are ignored. PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores any errors returned by these functions. If key is not present, PostFormValue returns the empty string.

func (*Request[T]) ProtoAtLeast

func (req *Request[T]) ProtoAtLeast(major, minor int) bool

ProtoAtLeast wraps the underlying request and reports whether the HTTP protocol used in the request is at least major.minor.

func (*Request[T]) Referer

func (req *Request[T]) Referer() string

Referer wraps the underlying request and returns the referring URL, if sent in the request.

func (*Request[T]) URL

func (req *Request[T]) URL() *url.URL

URL wraps http.Request.URL

func (*Request[T]) UserAgent

func (req *Request[T]) UserAgent() string

UserAgent wraps the underlying request and returns the client's User-Agent, if sent in the request.

func (*Request[T]) Value

func (req *Request[T]) Value(key any) any

Value returns the value associated with the request context for key, or nil if no value is associated with key. Successive calls to Value with the same key returns the same result.

type Response

type Response[T any] struct {
	// contains filtered or unexported fields
}

Response represents the response from an HTTP request.

func (*Response[T]) AddHeader added in v0.0.2

func (res *Response[T]) AddHeader(k string, v string)

Add adds the key, value pair to the header. It appends to any existing values associated with key. The key is case insensitive; it is canonicalized by CanonicalHeaderKey.

func (*Response[T]) SetBody added in v0.0.2

func (res *Response[T]) SetBody(t *T)

SetBody sets the response body

func (*Response[T]) SetHeader added in v0.0.2

func (res *Response[T]) SetHeader(k string, v string)

SetHeader sets the header entries associated with key to the single element value. It replaces any existing values associated with key. The key is case insensitive. It is canonicalized by textproto.CanonicalMIMEHeaderKey. To use non-canonical keys, assign to the map directly.

func (*Response[T]) SetMarshalFunc added in v0.0.3

func (res *Response[T]) SetMarshalFunc(fn MarshalFunc[T])

SetMarshalFunc sets the response marshal func. If a marshal function is supplied, it is prioritized over other implemented marshaler regardless of the content-type or accept headers set.

func (*Response[T]) SetStatus

func (res *Response[T]) SetStatus(statusCode int)

SetStatus sets the http status code of the response. StatusCode is ignored by the handler if it is not a valid http status code (i.e 1xx-5xx)

Directories

Path Synopsis
example module

Jump to

Keyboard shortcuts

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