middleware

package module
v0.0.6 Latest Latest
Warning

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

Go to latest
Published: Nov 30, 2024 License: MIT Imports: 10 Imported by: 0

README

middleware

middleware provides monadic HTTP middleware, enabling seamless composition of HTTP handlers with a functional approach. Built on top of warp, it supports monadic chaining, error handling, and structured responses, making middleware logic modular and composable.

Features

  • Monadic Middleware Composition: Uses monadic patterns to compose complex middleware chains.
  • Seamless Error Handling: Defines error-handling middleware with custom status codes and messages.
  • Flexible Data Validation: Supports payload validation with ozzo-validation, integrating smoothly with Go structs.

Installation

go get github.com/tetsuo/middleware

Usage

The following example shows how to define and chain middleware actions, using monadic composition to handle requests and responses effectively.

package main

import (
    "fmt"
    "net/http"
    "github.com/tetsuo/warp/result"
    w "github.com/tetsuo/middleware"
    validation "github.com/go-ozzo/ozzo-validation"
)

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

// Validate ensures Login meets length requirements.
func (d *user) Validate() error {
    return validation.ValidateStruct(d,
        validation.Field(&d.Login, validation.Required, validation.Length(2, 8)),
    )
}

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

var (
    decodeUserMiddleware   = w.DecodeBody[user]
    sendGreetingMiddleware = w.JSON[*greeting]
)

// greetingMiddlewareFromError sets a custom response on error.
func greetingMiddlewareFromError(err error) w.Middleware[*greeting] {
    return w.ApSecond(
        w.Status(202),
        w.FromResult(result.Ok(&greeting{Message: err.Error()})),
    )
}

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

// app chains middleware to handle requests or respond with validation errors.
var app = w.Chain(
    w.OrElse(
        w.Map(decodeUserMiddleware, greetingFromUser),
        greetingMiddlewareFromError,
    ),
    sendGreetingMiddleware,
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", w.ToHandlerFunc(app, onError))

    server := &http.Server{Addr: "localhost:8080", Handler: mux}
    if err := server.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)
}
Example Requests

With the following request, the server validates user.Login, enforcing a length between 2 and 8:

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

Response (validation error, status 202):

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

If the input is valid:

curl -s -d "login=tetsuo" -X POST "http://localhost:8080"

Response (greeting, status 200):

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

License

MIT License. See LICENSE for details.

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) warp.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) warp.Result[*A]

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

func GetOrElse

func GetOrElse[A any](ctx context.Context, ma warp.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) warp.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) warp.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 warp.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) warp.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 warp.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