middleware

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2023 License: MIT Imports: 9 Imported by: 0

README

middleware

A Middleware represents a computation which modifies a HTTP connection or reads from it, producing either a value of type A or an error for the next middleware in the pipeline.

type Middleware[A any] func(s *Connection) data.Result[A]

Example

The following example shows the use of monadic actions and sequentially composing them to write a response value.

Note that, middleware uses ozzo-validation library for decoding a request body (it accepts urlencoded forms and json as well). Simply, adding a Validate method on a struct type makes it validatable.

package main

import (
	"fmt"
	"net/http"

	validation "github.com/go-ozzo/ozzo-validation"
	"github.com/onur1/data/result"
	w "github.com/onur1/middleware"
)

// user is the expected request body.
type user struct {
	Login string `json:"login"`
}

// Validate ensures that a Login value is correctly set.
func (d *user) Validate() error {
	return validation.ValidateStruct(
		d,
		validation.Field(&d.Login, validation.Required, validation.Length(2, 8)),
	)
}

// greeting is the response value.
type greeting struct {
	Message string `json:"message"`
}

var (
  // decodeUserMiddleware decodes a request payload into a user struct and
  // returns a pointer to it.
	decodeUserMiddleware   = w.DecodeBody[user]
  // sendGreetingMiddleware sends a greeting as JSON.
	sendGreetingMiddleware = w.JSON[*greeting]
)

// greetingMiddlewareFromError creates a Middleware from an error which sets
// the status code to 202 and returns an error message as a result.
func greetingMiddlewareFromError(err error) w.Middleware[*greeting] {
	return w.ApSecond(
		w.Status(202),
		w.FromResult(result.Ok(&greeting{Message: err.Error()})),
	)
}

// greetingFromUser creates a new greeting from a user value.
func greetingFromUser(u *user) *greeting {
	return &greeting{
		Message: fmt.Sprintf("Hello, %s!", u.Login),
	}
}

// app middleware attempts decoding a request body in order to create a greeting
// message for the current user, falling back to a validation error message.
var app = w.Chain(
	w.OrElse(
		w.Map(
			decodeUserMiddleware,
			greetingFromUser,
		),
		greetingMiddlewareFromError,
	),
	sendGreetingMiddleware,
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/", w.ToHandlerFunc(app, onError))

	s := &http.Server{
		Addr:    "localhost:8080",
		Handler: mux,
	}

	if err := s.ListenAndServe(); err != nil {
		fmt.Printf("error: %v\n", err)
	}
}

func onError(err error, c *w.Connection) {
	fmt.Printf("uncaught error: %v\n", err)
	c.W.WriteHeader(500)
}

The Login attribute of a user struct must have a certain length, so this request will be responded with status 202 and a validation error message as JSON.

curl -s -d "login=x" -X POST "http://localhost:8080" | jq

Output:

{
  "message": "login: the length must be between 2 and 8."
}

Otherwise, a greeting message will be retrieved with status 200.

curl -s -d "login=onur1" -v  -X POST "http://localhost:8080" | jq

Output:

{
  "message": "Hello, onur1!"
}

Credits

Documentation

Overview

Package middleware implements the Middleware type.

Index

Constants

View Source
const (
	MediaTypeApplicationXML         = "application/xml"
	MediaTypeApplicationJSON        = "application/json"
	MediaTypeFormURLEncoded         = "application/x-www-form-urlencoded"
	MediaTypeImageGIF               = "image/gif"
	MediaTypeImageJPEG              = "image/jpeg"
	MediaTypeImagePNG               = "image/png"
	MediaTypeApplicationOctetStream = "application/octet-stream"
	MediaTypeTextHTML               = "text/html"
	MediaTypeTextXML                = "text/xml"
	MediaTypeTextCSV                = "text/csv"
	MediaTypeTextPlain              = "text/plain"
	MediaTypeMultipartFormData      = "multipart/form-data"
)

MIME types.

Variables

View Source
var ErrUnknownContentType = errors.New("unknown content-type")

ErrUnknownContentType is thrown by DecodeBody when a Content-Type is not one of "application/x-www-form-urlencoded" or "application/json".

Functions

func DecodeBody

func DecodeBody[A any](c *Connection) data.Result[*A]

DecodeBody middleware decodes (and optionally validates) a request payload into a value of type A.

func DecodeQuery

func DecodeQuery[A any](c *Connection) data.Result[*A]

DecodeQuery middleware decodes (and optionally validates) a value of type A from the query string.

func GetOrElse added in v0.0.3

func GetOrElse[A any](ma data.Result[A], onError func(error) A) A

GetOrElse creates a middleware which can be used to recover from a failing middleware with a new value.

func ToHandlerFunc

func ToHandlerFunc[A any](
	ma Middleware[A],
	onError func(error, *Connection),
) http.HandlerFunc

ToHandlerFunc turns a middleware into a standard http handler function.

Types

type Connection

type Connection struct {
	R *http.Request
	W http.ResponseWriter
	// contains filtered or unexported fields
}

A Connection represents the connection between an HTTP server and a user agent.

type Middleware

type Middleware[A any] func(*Connection) data.Result[A]

A Middleware represents a computation which modifies a HTTP connection or reads from it, producing either a value of type A or an error for the next middleware in the pipeline.

func Ap

func Ap[A, B any](fab Middleware[func(A) B], fa Middleware[A]) Middleware[B]

Ap creates a middleware by applying a function contained in the first middleware on the value contained in the second middleware.

func ApFirst

func ApFirst[A, B any](fa Middleware[A], fb Middleware[B]) Middleware[A]

ApFirst creates a middleware by combining two effectful computations on a connection, keeping only the result of the first.

func ApSecond

func ApSecond[A, B any](fa Middleware[A], fb Middleware[B]) Middleware[B]

ApSecond creates a middleware by combining two effectful computations on a connection, keeping only the result of the second.

func Chain

func Chain[A, B any](ma Middleware[A], f func(A) Middleware[B]) Middleware[B]

Chain creates a middleware which combines two results in sequence, using the return value of one middleware to determine the next one.

func ChainFirst

func ChainFirst[A, B any](ma Middleware[A], f func(A) Middleware[B]) Middleware[A]

ChainFirst composes two middlewares in sequence, using the return value of one to determine the next one, keeping only the result of the first one.

func ContentType

func ContentType[A any](contentType string) Middleware[A]

ContentType creates a middleware which sets the Content-Type header on a response.

func DecodeHeader

func DecodeHeader(name string, rules ...validation.Rule) Middleware[string]

DecodeHeader creates a middleware by validating a string value from a header.

func DecodeMethod

func DecodeMethod[A any](f func(string) data.Result[A]) Middleware[A]

DecodeMethod creates a Middleware by applying a function on a request method.

func FilterOrElse

func FilterOrElse[A any](ma Middleware[A], predicate data.Predicate[A], onFalse func(A) error) Middleware[A]

FilterOrElse creates a middleware which can be used to fail with an error unless a predicate holds on a succeeding result.

func FromRequest

func FromRequest[A any](f func(c *http.Request) data.Result[A]) Middleware[A]

FromRequest creates a middleware for reading a request by applying a function on a connection request that either yields a value of type A, or fails with an error.

func FromResult

func FromResult[A any](ra data.Result[A]) Middleware[A]

FromResult converts a function, which takes no parameters and returns a value of type A along with an error, into a Middleware.

func HTML

func HTML(html string) Middleware[any]

HTML creates a middleware that sends a string as HTML response.

func Header(name, value string) Middleware[any]

Header creates a middleware that sets a header on the response. Note that, changing a header after a call to Status has no effect.

func JSON

func JSON[A any](d A) Middleware[any]

JSON sends a JSON object as response.

func Map

func Map[A, B any](fa Middleware[A], f func(A) B) Middleware[B]

Map creates a middleware by applying a function on a succeeding middleware.

func MapError

func MapError[A any](fa Middleware[A], f func(error) error) Middleware[A]

MapError creates a middleware by applying a function on a failing middleware.

func ModifyResponse

func ModifyResponse[A any](f func(w http.ResponseWriter)) Middleware[A]

ModifyResponse creates a middleware for writing a response.

func OrElse

func OrElse[A any](ma Middleware[A], onError func(error) Middleware[A]) Middleware[A]

OrElse creates a middleware which can be used to recover from a failing middleware by switching to a new middleware.

func PlainText

func PlainText(text string) Middleware[any]

PlainText creates a middleware that sends a plain text as response.

func Redirect

func Redirect(url string, code int) Middleware[any]

Redirect creates a middleware for redirecting a request to the given URL with the given 3xx code.

func Status

func Status(status int) Middleware[any]

Status creates a middleware that sets a response status code.

func Write

func Write(body []byte) Middleware[any]

Write creates a middleware for sending the given byte array response without specifying the Content-Type.

Jump to

Keyboard shortcuts

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