httpm

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Mar 24, 2020 License: MIT Imports: 9 Imported by: 0

README

httpm

Godoc

httpm is the middleman subpackage specialized in http.

Motivation

As golang developer, we often thrive under the weight of error checking. Though error management in go ensures that the code is resillient, it also makes it very verbose, and bubbeling errors up in the stack adds a lot of boilerplate.

// Parse content from HTTP request, get header, get params...
raw, err := ioutil.ReadAll(r.Body)
if err != nil {
	// ...
}
err := json.Unmarshal(raw, &payload)
if err != nil {
	// ...
}
err := decodeURIParams(r, &params)
if err != nil {
	// ...
}

// ...

// Start business logic

This package provides functions and functors that can be used to prevent this.

Documentation

Overview

Package httpm provides functions to produce and consume http messages.

It's main goal is to reduce the boilerplate due to:
* writing request and reading response on client side
* reading request and writing response on server side

The ComposeXXX functions return the possible error that occures during composition,
leaving a very clean describtion of what to do when composing or consuming a message.

// Classic way ...
raw, err := ioutil.ReadAll(r.Body)
if err != nil {
	...
}
if err := json.Unmarshal(raw, &target); err != nil {
	...
}
if err := check(target); err != nil {
	...
}
if err := parseParams(r.URL, &params); err != nil {
	...
}

// With httpm ...
parse := httpm.ComposeRequest(
	httpm.ReadRequestBody(httpm.DecodeAndCheck(json.Unmarshal, check),
	httpm.ReadRequestParams(parseParams),
)
r, err := parse(r)
if err != nil {
	...
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeText

func DecodeText(raw []byte, v interface{}) error

DecodeText decodes the raw data into the input. Input must be of type *[]byte, *string, or encoding.TextUnmarshaler

func EncodeText

func EncodeText(v interface{}) ([]byte, error)

EncodeText encodes string, []byte and text.Marshaller implementations.

func ExtendStatusCoder

func ExtendStatusCoder(next http.Handler) http.Handler

ExtendStatusCoder is a middleware that extends the current http.ResponseWriter into a StatusCoder.

func NewSender

func NewSender(client Doer) func(RequestFn, ResponseFn) error

NewSender returns a function that sends a request and handling its response with the given doer. See Send for details.

func ReadRequestBody

func ReadRequestBody(d Decoder) func(into interface{}) RequestFn

ReadRequestBody decodes the request body.

func ReadRequestParams

func ReadRequestParams(fn ParamParser) func(interface{}) RequestFn

ReadRequestParams decodes the request parameters.

func ReadResponseBody

func ReadResponseBody(d Decoder) func(into interface{}) ResponseFn

ReadResponseBody decodes the body.

func Send

func Send(in RequestFn, out ResponseFn) error

Send a request and handles its response with the http.DefaultClient.

If in is nil, Send returns an error.
If out is nil, the response is not handled. This can be useful for fire & forget usages.

func WriteRequestBody

func WriteRequestBody(e Encoder) func(input interface{}) RequestFn

WriteRequestBody encodes and writes the given input in the body.

func WriteResponseWriterBody

func WriteResponseWriterBody(e Encoder) func(interface{}) ResponseWriterFn

WriteResponseWriterBody encodes and writes the given input in the body.

Types

type Caller added in v0.3.0

type Caller interface {
	Call(ctx context.Context, method, url string, h http.Header, in, out interface{}) error
}

Caller is the client side interface responsible for doing HTTP calls.

func Chain added in v0.3.0

func Chain(caller Caller, ms ...ClientMiddleware) Caller

Chain the middlewares around the caller. Chain(a, b, c).Call(ctx, method, url, h, in, out) is the equivalent to (c(b(a(ctx, method, url, h, in, out)))

type CallerFunc added in v0.3.0

type CallerFunc func(ctx context.Context, method, url string, h http.Header, in, out interface{}) error

CallerFunc is a function that can be used as a caller.

func (CallerFunc) Call added in v0.3.0

func (c CallerFunc) Call(ctx context.Context, method, url string, h http.Header, in, out interface{}) error

Call implements the Caller interface.

type Checker

type Checker = func(interface{}) error

Checker is a func that checks the validity of an input.

type ClientMiddleware added in v0.3.0

type ClientMiddleware = func(Caller) Caller

ClientMiddleware is a function that chains caller.

type Decoder

type Decoder func([]byte, interface{}) error

Decoder describes a type capable of decoding a []byte.

func DecodeAndCheck

func DecodeAndCheck(decode Decoder, check Checker) Decoder

DecodeAndCheck the input.

type Doer

type Doer interface {
	Do(*http.Request) (*http.Response, error)
}

Doer describes a type capable of sending Requests and returning responses. *http.Client is the cannonical implementation of a Doer.

type Encoder

type Encoder func(interface{}) ([]byte, error)

Encoder describes a type capable of encoding a value into a []byte.

type ParamParser

type ParamParser = func(interface{}, url.Values) error

ParamParser designates a function capable of parsing URL params.

type RequestFn

type RequestFn = func(*http.Request) (*http.Request, error)

RequestFn describes a function that can be member of a chainable Request handling.

func ComposeRequest

func ComposeRequest(fns ...RequestFn) RequestFn

ComposeRequest composes a list of RequestFn into one. ComposeRequest(foo, bar) is functionnally equivalent to bar(foo(r))

Example (Client)
package main

import (
	"log"
	"net/http"

	"github.com/moxar/middleman/httpm"
)

func main() {

	var encode httpm.Encoder // json.Marshal

	NewRequest := func(path, url string, input interface{}) (*http.Request, error) {
		return httpm.ComposeRequest(
			httpm.NewRequest(path, url),
			httpm.WriteRequestBody(encode)(input),
		)(nil)
	}

	type Hero struct {
		Name     string
		Universe string
	}

	// Compose request.
	batman := Hero{Name: "Batman", Universe: "DC"}
	r, err := NewRequest("POST", "https://api.superheroes.com/heroes", batman)
	if err != nil {
		log.Println(err)
	}

	// use r
	_ = r
}
Output:

Example (Server)
package main

import (
	"net/http"

	"github.com/moxar/middleman/httpm"
)

func main() {

	var parseParams httpm.ParamParser // gorilla/schema.NewDecoder().Decode
	var check httpm.Checker           // asaskevich/govalidator.ValidateStruct
	var decode httpm.Decoder          // json.Unmarshal

	// parseRequest parses the request params (?foo=bar) and body.
	parseRequest := func(r *http.Request, body, params interface{}) error {
		_, err := httpm.ComposeRequest(
			httpm.ReadRequestBody(httpm.DecodeAndCheck(decode, check))(body),
			httpm.ReadRequestParams(parseParams)(params),
		)(r)
		return err
	}

	type Hero struct {
		Name     string
		Universe string
	}

	type Params struct {
		FastInsert bool
	}

	// HTTP Handler...
	handle := func(w http.ResponseWriter, r *http.Request) {
		var hero Hero
		var params Params
		if err := parseRequest(r, &hero, &params); err != nil {
			// ...
		}

		// use hero and params values
	}

	_ = handle
}
Output:

func NewRequest

func NewRequest(method, url string) RequestFn

NewRequest prepares a http request with the standard http.NewRequest method.

func SetRequestContext added in v0.1.0

func SetRequestContext(ctx context.Context) RequestFn

SetRequestContext adds the context to the request.

func SetRequestHeader added in v0.2.0

func SetRequestHeader(h http.Header) RequestFn

SetRequestHeader sets the header of the request.

type ResponseFn

type ResponseFn = func(*http.Response) (*http.Response, error)

ResponseFn describes a function that can be member of a chainable Response handling.

func ComposeResponse

func ComposeResponse(fns ...ResponseFn) ResponseFn

ComposeResponse composes a list of ResponseFns into one. ComposeResponse(foo, bar) is functionnally equivalent to bar(foo(w))

Example (Client)
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/moxar/middleman/httpm"
)

func main() {

	FailOver400 := func(s int) error {
		if s <= 400 {
			return nil
		}
		return errors.New(http.StatusText(s))
	}

	// Compose response.
	ParseResponse := func(r *http.Response, status *int, payload *string) error {

		_, err := httpm.ComposeResponse(
			httpm.ReadResponseStatus(status),
			httpm.ReturnErrorFromResponseStatus(FailOver400),
			httpm.ReadResponseBody(httpm.DecodeText)(payload),
		)(r)
		return err
	}

	r := &http.Response{
		Body:       ioutil.NopCloser(strings.NewReader(`some payload`)),
		StatusCode: 305,
	}

	var out string
	var status int
	err := ParseResponse(r, &status, &out)
	if err != nil {
		log.Println(err)
	}

	fmt.Println(out)
	fmt.Println(status)

	// use r
	_ = r

}
Output:

some payload
305

func ReadResponseStatus

func ReadResponseStatus(status *int) ResponseFn

ReadResponseStatus into status.

func ReturnErrorFromResponseStatus

func ReturnErrorFromResponseStatus(f func(status int) error) ResponseFn

ReturnErrorFromResponseStatus returns an error depending on the response status.

type ResponseWriterFn

type ResponseWriterFn = func(w http.ResponseWriter) http.ResponseWriter

ResponseWriterFn describes a function that can be member of a chainable ResponseWriter handling.

func ComposeResponseWriter

func ComposeResponseWriter(fn ...ResponseWriterFn) ResponseWriterFn

ComposeResponseWriter composes a list of ResponseWriterFn into one. ComposeResponseWriter(foo, bar) is functionnally equivalent to bar(foo(r))

Example (Server)
package main

import (
	"net/http"

	"github.com/moxar/middleman/httpm"
)

func main() {
	writeResponse := func(w http.ResponseWriter, payload interface{}, status int) {
		httpm.ComposeResponseWriter(
			httpm.WriteResponseWriterStatus(status),
			httpm.WriteResponseWriterBody(httpm.EncodeText)(payload),
		)(w)
	}

	var w http.ResponseWriter
	writeResponse(w, "some payload", http.StatusNoContent)

}
Output:

func WriteResponseWriterStatus

func WriteResponseWriterStatus(status int) ResponseWriterFn

WriteResponseWriterStatus writes the input status code as header.

type StatusCoder

type StatusCoder interface {
	StatusCode() int
}

StatusCoder returns the StatusCode.

Jump to

Keyboard shortcuts

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