Categorygithub.com/onur1/middleware
modulepackage
0.0.4
Repository: https://github.com/onur1/middleware.git
Documentation: pkg.go.dev

# 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) warp.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/warp/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

# Functions

Ap creates a middleware by applying a function contained in the first middleware on the value contained in the second middleware.
ApFirst creates a middleware by combining two effectful computations on a connection, keeping only the result of the first.
ApSecond creates a middleware by combining two effectful computations on a connection, keeping only the result of the second.
Chain creates a middleware which combines two results in sequence, using the return value of one middleware to determine the next one.
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.
ContentType creates a middleware which sets the Content-Type header on a response.
DecodeBody middleware decodes (and optionally validates) a request payload into a value of type A.
DecodeHeader creates a middleware by validating a string value from a header.
DecodeMethod creates a Middleware by applying a function on a request method.
DecodeQuery middleware decodes (and optionally validates) a value of type A from the query string.
FilterOrElse creates a middleware which can be used to fail with an error unless a predicate holds on a succeeding result.
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.
FromResult converts a function, which takes no parameters and returns a value of type A along with an error, into a Middleware.
GetOrElse creates a middleware which can be used to recover from a failing middleware with a new value.
Header creates a middleware that sets a header on the response.
HTML creates a middleware that sends a string as HTML response.
JSON sends a JSON object as response.
Map creates a middleware by applying a function on a succeeding middleware.
MapError creates a middleware by applying a function on a failing middleware.
ModifyResponse creates a middleware for writing a response.
OrElse creates a middleware which can be used to recover from a failing middleware by switching to a new middleware.
PlainText creates a middleware that sends a plain text as response.
Redirect creates a middleware for redirecting a request to the given URL with the given 3xx code.
Status creates a middleware that sets a response status code.
ToHandlerFunc turns a middleware into a standard http handler function.
Write creates a middleware for sending the given byte array response without specifying the Content-Type.

# Constants

# Variables

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

# Structs

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

# Type aliases

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.