chainist

package module
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2022 License: MIT Imports: 1 Imported by: 0

README

chainist

GoDoc Go Report Card MIT licensed Test Codecov

chainist is a simple http handler, or middleware, chaining library for golang.

Underlying Concepts

concepts.png

Installation

go get github.com/t-katsumura/chainist@latest

Basic Usage

Create a new chain.
Http handlers or middleware can be added this time.

chain := chainist.NewChain()
// or
chain := chainist.NewChain(handler1, handler2)

Add http handlers, i.e. func(next http.Handler) http.Handler, to the chain.

// add handlers one by one
chain.Append(handler1)
chain.Append(handler2)

// add handlers using method chaining
chain.Append(handler1).Append(handler2)

// add handlers at a time
chain.Extend(handler1, handler2)

Add http handler function, i.e. func(w http.ResponseWriter, r *http.Request), to the chain.
Here, 'Pre' means the functions will be executed before calling succeeding handlers. 'Post' means the functions will be executed after calling succeeding handlers.

// add handlerFuncss one by one
chain.AppendPreFunc(handlerFunc1)
chain.AppendPreFunc(handlerFunc2)

// add handlerFuncs using method chaining
chain.AppendPreFunc(handlerFunc1).AppendPreFunc(handlerFunc2)

// add handlerFuncs at a time
chain.ExtendPreFunc(handlerFunc1, handlerFunc2)
// add handlerFuncs one by one
chain.AppendPostFunc(handlerFunc1)
chain.AppendPostFunc(handlerFunc2)

// add handlerFuncs using method chaining
chain.AppendPostFunc(handlerFunc1).AppendPostFunc(handlerFunc2)

// add handlerFuncs at a time
chain.ExtendPostFunc(handlerFunc1, handlerFunc2)

Get the handler chain which type is http.Handler.

chain.Chain()

// or with handler function
chain.ChainFunc(handlerFunc)

Example

This is an example of chainist. With chainist, you don't need to wrap handlerFunc using http.Handler and http.HandlerFunc. chainist internally wrap your handlerFunc.

package main

import (
    "net/http"

    "github.com/t-katsumura/chainist"
)

// sample http handlerFunc
func handlerFunc1(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Hi from handlerFunc 1!\n"))
}

// sample http handlerFunc
func handlerFunc2(w http.ResponseWriter, _ *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Hi from handlerFunc 2!\n"))
}

// sample http handler
func handler1(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hi from handler 1!\n"))
        next.ServeHTTP(w, r)
    })
}

// sample http handler
func handler2(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hi from handler 2!\n"))
        next.ServeHTTP(w, r)
    })
}

func main() {

    // create a new chain
    chain := chainist.NewChain()

    // add handler functions
    chain.AppendPreFunc(handlerFunc1)
    chain.AppendPostFunc(handlerFunc2)

    // add handlers
    chain.Extend(handler1, handler2)

    // Run http server which responds
    /*
        Hi from handlerFunc 1!
        Hi from handler 1!
        Hi from handler 2!
        Hi from handlerFunc 2!
    */
    http.ListenAndServe(":8080", chain.Chain())
}

Questions and support

All bug reports, questions and suggestions should go though Github Issues.

Contributing

  1. Fork it
  2. Create feature branch (git checkout -b feature/new-feature)
  3. Write codes on feature branch
  4. Commit your changes (git commit -m "Added new feature")
  5. Push to the branch (git push -u origin feature/new-feature)
  6. Create new Pull Request on Github

Development

  • Write codes
  • go fmt -x ./... - format codes
  • go test -v -cover ./... - run test and the coverage should always be 100%

When generating coverage report, run

  1. go test -cover ./... -coverprofile=cover.out
  2. go tool cover -html=cover.out -o cover.html

Similar packages

alice is a famous middleware chaining library. This project is inspired by alice.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chain

type Chain struct {
	// Middleware is the list of middleware.
	// This can contain nil values but they are ignored
	// when getting middleware chains with Chain() or ChainFunc().
	Middleware []Middleware

	// HandlerFunc is the http handler function at the edge of the chain.
	// If it is not set before calling Chain() or ChainFunc(),
	HandlerFunc http.HandlerFunc
}

Chain is the struct for middleware chain. This holds the set of some middleware (i.e. func(h http.Handler) http.Handler) and a http handler function (i.e. `func(http.ResponseWriter, *http.Request)`)

Example
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
)

func myHandlerFunc0(w http.ResponseWriter, _ *http.Request) {
	w.WriteHeader(http.StatusOK)
	if _, err := w.Write([]byte("Hi from myHandlerFunc0!\n")); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func myHandlerFunc1(w http.ResponseWriter, _ *http.Request) {
	w.WriteHeader(http.StatusOK)
	if _, err := w.Write([]byte("Hi from myHandlerFunc1!\n")); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func myHandlerFunc2(w http.ResponseWriter, _ *http.Request) {
	w.WriteHeader(http.StatusOK)
	if _, err := w.Write([]byte("Hi from myHandlerFunc2!\n")); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func myHandler1(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if _, err := w.Write([]byte("Hi from myHandler1!\n")); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
		}
		if next != nil {
			next.ServeHTTP(w, r)

		}
	})
}

func myHandler2(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if _, err := w.Write([]byte("Hi from myHandler2!\n")); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
		}
		next.ServeHTTP(w, r)
	})
}

func main() {

	// create a new chain struct
	chain := NewChain()

	// add handler functions
	chain.AppendPreFunc(myHandlerFunc1)
	chain.AppendPostFunc(myHandlerFunc2)
	// add handlers
	chain.Extend(myHandler1, myHandler2)
	chain.SetHandlerFunc(myHandlerFunc0)

	// Run http server
	ts := httptest.NewServer(chain.Chain())
	defer ts.Close()

	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
	if err != nil {
		// handle error
	}

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		// handle error
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		// handle error
	}

	resp.Body.Close()
	s := string(body)
	fmt.Println(s)

}
Output:
Hi from myHandlerFunc1!
Hi from myHandler1!
Hi from myHandler2!
Hi from myHandlerFunc0!
Hi from myHandlerFunc2!

func NewChain

func NewChain(ms ...Middleware) *Chain

NewChain creates a new middleware chain.

chain := chainist.NewChain()

Middleware can be added at the time of creation of a chain. Middleware is a function with the signature of `func(h http.Handler) http.Handler`. If nil is contained in the given arguments, nil is returned.

// handler1 is called at first, and handler3 at last
chain := chainist.NewChain(handler1, handler2, handler3)

func (*Chain) Append

func (c *Chain) Append(m Middleware) *Chain

Append middleware at the last of the chain. If nil is given, then the chain will be returned without adding it.

chain := chainist.NewChain()
chain.Append(handler1)
     .Append(handler2)
     .Append(handler3)

func (*Chain) AppendPostFunc

func (c *Chain) AppendPostFunc(f http.HandlerFunc) *Chain

AppendPostFunc appends a http handler function as post-executable function which is executed after invoked succeeding middleware. Handler function must have the signature of `func(w http.ResponseWriter, r *http.Request)`. If nil is given as handler function, then the chain will be returned as it is.

If you pass `YourPostHandlerFunc(w http.ResponseWriter, r *http.Request)` as an argument, it is treaded as the middleware of

func handler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if next != nil {
            // invoke the next handler function before YourPostHandlerFunc
            next.ServeHTTP(w, r)
        }
        YourPostHandlerFunc(w, r)
    })
}

Usage:

chain := chainist.NewChain()
chain.AppendPostFunc(handlerFunc1)
     .AppendPostFunc(handlerFunc2)
     .AppendPostFunc(handlerFunc3)

func (*Chain) AppendPreFunc

func (c *Chain) AppendPreFunc(f http.HandlerFunc) *Chain

AppendPreFunc appends a http handler function as pre-executable function which is executed before invoking succeeding middleware. Handler function must have the signature of `func(w http.ResponseWriter, r *http.Request)`. If nil is given as handler function, then the chain will be returned as it is.

If you pass `YourPreHandlerFunc(w http.ResponseWriter, r *http.Request)` as an argument, it is treaded as the middleware of

func handler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        YourPreHandlerFunc(w, r)
        if next != nil {
            // invoke the next handler function after YourPreHandlerFunc
            next.ServeHTTP(w, r)
        }
    })
}

Usage example

chain := chainist.NewChain()

chain.AppendPreFunc(handlerFunc1)
     .AppendPreFunc(handlerFunc2)
     .AppendPreFunc(handlerFunc3)

func (*Chain) Chain

func (c *Chain) Chain() http.Handler

Chain returns a new middleware chain. This function returns nil if there is no middleware and no handler function.

Usage:

// defining a middleware chain
// this create the chain of [handlerFunc1, handlerFunc2, handler1, handler2]
chain := chainist.NewChain()
chain.AppendPreFunc(handlerFunc1)
chain.AppendPostFunc(handlerFunc2)
chain.Extend(handler1, handler2)

// get a new handler from chain
handler := chain.Chain()
if handler == nil {
    panic("handler is nil")
}

// run http server
http.ListenAndServe(":8080", handler)

func (*Chain) ChainFunc

func (c *Chain) ChainFunc(f http.HandlerFunc) http.Handler

ChainFunc returns a new middleware chain with a handler function. If the given handler function is not nil, it is used instead of the chain.HandlerFunc which is set with `SetHandlerFunc()`. If both given handler function and chain.HandlerFunc are nil, nil value is passed to the middleware. Under that situation, your middleware have to be coded to handle nil for the http.Handler given as the middleware's argument.

Usage:

// defining a middleware chain
chain := chainist.NewChain()
chain.AppendPreFunc(handlerFunc1)
chain.AppendPostFunc(handlerFunc2)
chain.Extend(handler1, handler2)

// get a new handler from chain
handler := chain.ChainFunc(handlerFuncAtEdge)
if handler == nil {
    panic("handler is nil")
}

// run http server
http.ListenAndServe(":8080", handler)

func (*Chain) Extend

func (c *Chain) Extend(ms ...Middleware) *Chain

Extend appends multiple middleware at a time. This function append multiple middleware at the end of the chain. If nil is contained in the arguments, they are ignored.

// create a middleware chain
chain := chainist.NewChain(handler1, handler2)

// append handler3 and handler4 with this order
// chain will have handler1,handler2,handler3,handler4 with this order
chain.Extend(handler3, handler4)

func (*Chain) ExtendPostFunc

func (c *Chain) ExtendPostFunc(fs ...http.HandlerFunc) *Chain

ExtendPostFunc appends multiple post-executable handler functions at a time. This function append multiple handler function at the end of the chain. nil is ignored if contained in the arguments.

// create a middleware chain
chain := chainist.NewChain(handler1, handler2)

// append middleware created with handlerFunc3 and handlerFunc4 with this order
// chain will have handler1,handler2,handler3,handler4 with this order
chain.ExtendPostFunc(handlerFunc3, handlerFunc4)

func (*Chain) ExtendPreFunc

func (c *Chain) ExtendPreFunc(fs ...http.HandlerFunc) *Chain

ExtendPreFunc appends multiple pre-executable handler functions at a time. This function append multiple handler function at the end of the chain. nil is ignored if contained in the arguments.

// create a middleware chain
chain := chainist.NewChain(handler1, handler2)

// append middleware created with handlerFunc3 and handlerFunc4 with this order
// chain will have handler1,handler2,handler3,handler4 with this order
chain.ExtendPreFunc(handlerFunc3, handlerFunc4)

func (*Chain) Insert

func (c *Chain) Insert(m Middleware, i int) *Chain

Insert inserts middleware at designated position of the chain. Middleware must have the signature of `func(h http.Handler) http.Handler`. If nil is given as middleware, then the chain will be returned without adding it.

If the number less than 0 is given as the position, given handler is added at the first of the chain. If the number grater than the length of middleware is given as the position, given handler is added at the last of the chain.

Usage:

// create a middleware chain with the order of handler1,handler2,handler3
chain := chainist.NewChain(handler1, handler2, handler3)

// insert handler4 between handler1 and handler2
// chain.Middleware[1] will be handler4
chain.Insert(handler4, 1)

// insert handler5 at the first of the chain
// chain.Middleware[0] will be handler4
chain.Insert(handler5, 0)

func (*Chain) InsertPostFunc

func (c *Chain) InsertPostFunc(f http.HandlerFunc, i int) *Chain

Insert a post-executable handler function at designated number of chain. Handler function must have the signature of `func(w http.ResponseWriter, r *http.Request)`. If nil is given as handler function, the chain will be returned without inserting it.

If the number less than 0 is given as the position, given http handler function is added at the first of the chain. If the number grater than the length of middleware is given as the position, the given http handler function is added at the last of the chain.

// create a middleware chain with the order of handler1,handler2,handler3
chain := chainist.NewChain(handler1, handler2, handler3)

// insert handlerFunc4 between handler1 and handler2
// chain.Middleware[1] will be a middleware created with handlerFunc4
chain.InsertPostFunc(handlerFunc4, 1)

// insert handlerFunc5 at the first of the chain
// chain.Middleware[0] will be a middleware created with handlerFunc5
chain.InsertPostFunc(handlerFunc5, 0)

func (*Chain) InsertPreFunc

func (c *Chain) InsertPreFunc(f http.HandlerFunc, i int) *Chain

Insert a pre-executable handler function at designated number of chain. Handler function must have the signature of `func(w http.ResponseWriter, r *http.Request)`. If nil is given as handler function, the chain will be returned without inserting it.

If the number less than 0 is given as the position, given http handler function is added at the first of the chain. If the number grater than the length of middleware is given as the position, the given http handler function is added at the last of the chain.

// create a middleware chain with the order of handler1,handler2,handler3
chain := chainist.NewChain(handler1, handler2, handler3)

// insert handlerFunc4 between handler1 and handler2
// chain.Middleware[1] will be a middleware created with handlerFunc4
chain.InsertPreFunc(handlerFunc4, 1)

// insert handlerFunc5 at the first of the chain
// chain.Middleware[0] will be a middleware created with handlerFunc5
chain.InsertPreFunc(handlerFunc5, 0)

func (*Chain) Join

func (c *Chain) Join(o *Chain) *Chain

Join joins two chains.

// create two chains with handlers.
chain1 := chainist.NewChain(handler1, handler2)
chain2 := chainist.NewChain(handler3, handler4)

// join two chains
// this operation extends chain1 with chain2
// chain1 will have handler1,handler2,handler3,handler4 with this order
chain1.Join(chain2)

func (*Chain) Len

func (c *Chain) Len() int

Len gets the length of middleware chain. This contains the length of Middleware and the Handler Function if it's already set.

chain := chainist.NewChain(handler1, handler2)
chain.SetHandlerFunc(handlerFuncAtEdge)

// this shows 3
println(chain.len())

func (*Chain) SetHandlerFunc

func (c *Chain) SetHandlerFunc(f http.HandlerFunc) *Chain

SetHandlerFunc sets the handler function which will be invoked at the edge of the chain. If nil is given as the argument, it is ignored.

// create a middleware chain with handler1 and handler2
chain := chainist.NewChain(handler1, handler2)

// set the handler function of handlerFuncAtEdge
chain.SetHandlerFunc(handlerFuncAtEdge)

type HandlerFuncWrapper

type HandlerFuncWrapper struct {
	HandlerFunc http.HandlerFunc
}

func (*HandlerFuncWrapper) Middleware added in v0.0.4

func (h *HandlerFuncWrapper) Middleware(next http.Handler) http.Handler

Alias for `PreMiddleware`

func (*HandlerFuncWrapper) PostMiddleware added in v0.0.4

func (h *HandlerFuncWrapper) PostMiddleware(next http.Handler) http.Handler

Wrap http handler function as http handler. Wrapped function is executed after invoking proceeding handlers. This is what `Post` means.

func (*HandlerFuncWrapper) PreMiddleware added in v0.0.4

func (h *HandlerFuncWrapper) PreMiddleware(next http.Handler) http.Handler

Wrap http handler function as http handler. Wrapped function is executed before invoking proceeding handlers. This is what `Pre` means.

type Middleware added in v0.0.6

type Middleware func(h http.Handler) http.Handler

Define the "middleware" type. Note, this is the commonly used definition of middleware.

An example of basic definition of middleware is

func YourMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // you can write your code here
        if next != nil {
            next.ServeHTTP(w, r)
        }
        // you can write your code here
    })
}

Jump to

Keyboard shortcuts

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