juliet

package module
v0.0.0-...-63378c6 Latest Latest
Warning

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

Go to latest
Published: Feb 11, 2016 License: MIT Imports: 2 Imported by: 0

README

Juliet is a lightweight middleware chaining helper that pass a Context (map) object from a middleware to the next one.

This work is inspired by Stack by Alex Edwards and Alice by Justinas Stankevicius.

Godoc is here : https://godoc.org/github.com/root-gg/juliet

And there is a working example in the examples package :

package main

import (
	"net/http"
	"log"
	"net"
	"fmt"

	"github.com/root-gg/juliet"
)

// Juliet is a lightweight middleware chaining helper that pass a Context (map) object
// from a middleware to the next one.
//
// Middlewre is a pattern where http request/response are passed through many handlers to reuse code.

// For example this classic middleware log the requested url
func logMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Our middleware logic goes here...
		log.Println(r.URL.String())

		// Pass the request to the next middleware / handler
		next.ServeHTTP(w, r)
	})
}

// Juliet adds a context parameter to the middleware pattern that will be passed along the Chain.
// For example this middleware puts the request's source IP address in the context.
func getSourceIpMiddleware(ctx *juliet.Context, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// Get the source IP from the request remote address
		ip, _, err := net.SplitHostPort(r.RemoteAddr)

		// You can handle failure at any point of the chain by not calling next.ServeHTTP
		if err != nil {
			http.Error(w, "Unable to parse source ip address", 500)
			return
		}

		// Save the source ip in the context
		ctx.Set("ip", ip)

		// Pass the request to the next middleware / handler
		next.ServeHTTP(w, r)
	})
}

// As a context is nothing more that a map[interface{}]interface{} with syntactic sugar you have to ensure you
// check types of values you get from the context. To keep your code clean you can write helpers to do that and keep
// type safety everywhere.
func getSourceIp(ctx *juliet.Context) string {
	if sourceIP, ok := ctx.Get("ip"); ok {
		return sourceIP.(string)
	}
	return ""
}

// The last link of a middleware chain is the application Handler
func pingHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("pong\n"))
}

// Juliet can also pass the context parameter to application Handlers
func ipHandler(ctx *juliet.Context, resp http.ResponseWriter, req *http.Request) {
	// write http response
	resp.Write([]byte(fmt.Sprintf("your IP address is : %s\n", getSourceIp(ctx))))
}

func main(){
	// Juliet links middleware and handler with chain objects
	chain := juliet.NewChain()

	// Chain objects are immutable and any operation on it returns a new chain object.
	// You can append one or more middleware to the chain at a time using the Append method.
	chain = chain.Append(getSourceIpMiddleware)

	// You can append a middleware to the beginning of the chain with the AppendChain method.
	// When working with a non context-aware ( Juliet ) middleware you have to use the Adapt method.
	chain = juliet.NewChain(juliet.Adapt(logMiddleware)).AppendChain(chain)

	// Now we have this middleware chain : logUrl > getSourceIp > ApplicationHandler
	// We could have built it in one pass this way :
	//  chain := juliet.NewChain(juliet.Adapt(logMiddleware),getSourceIpMiddleware)

	// It's now time to add some application handlers and to bind everything to some HTTP route.

	// With a context handler
	http.Handle("/ip", chain.Then(ipHandler))

	// With a classic http.HandlerFunc
	http.Handle("/ping", chain.ThenHandlerFunc(pingHandler))

	// With a classic http.Handler
	http.Handle("/404", chain.ThenHandler(http.NotFoundHandler()))

	log.Fatal(http.ListenAndServe(":1234", nil))
}

// $ go run main.go
// 2016/02/01 12:20:39 /ip
// 2016/02/01 12:20:44 /ping
// 2016/02/01 12:20:56 /404
//
// $ curl 127.0.0.1:1234/ip
// your IP address is : 127.0.0.1
// $ curl 127.0.0.1:1234/ping
// pong
// $ curl 127.0.0.1:1234/404
// 404 page not found

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chain

type Chain struct {
	// contains filtered or unexported fields
}

Chain link context middlewares to each other. Chain are immutable and any operation on them returns a new Chain object.

func NewChain

func NewChain(cms ...ContextMiddleware) (chain *Chain)

NewChain creates a new contextMiddleware chain.

func (*Chain) Append

func (chain *Chain) Append(cms ...ContextMiddleware) (newChain *Chain)

Append add contextMiddleware(s) to the chain.

func (*Chain) AppendChain

func (chain *Chain) AppendChain(tail *Chain) (newChain *Chain)

AppendChain duplicates a chain and links it to the current chain.

func (*Chain) Then

func (chain *Chain) Then(fn ContextHandlerFunc) (ch *ChainHandler)

Then add a ContextHandlerFunc to the end of the chain and returns a http.Handler compliant ContextHandler

func (*Chain) ThenHandler

func (chain *Chain) ThenHandler(handler http.Handler) (ch *ChainHandler)

ThenHandler add a http.Handler to the end of the chain and returns a http.Handler compliant ContextHandler

func (*Chain) ThenHandlerFunc

func (chain *Chain) ThenHandlerFunc(fn func(http.ResponseWriter, *http.Request)) (ch *ChainHandler)

ThenHandlerFunc add a http.HandlerFunc to the end of the chain and returns a http.Handler compliant ContextHandler

type ChainHandler

type ChainHandler struct {
	// contains filtered or unexported fields
}

ChainHandler holds a chain and a final handler. It satisfy the http.Handler interface and can be served directly by a net/http server.

func (*ChainHandler) ServeHTTP

func (ch *ChainHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request)

ServeHTTP builds the chain of handlers in order, closing the context along the way and executes it.

type Context

type Context struct {
	// contains filtered or unexported fields
}

Context hold a map[interface{}]interface{} to pass along the middleware chain.

func NewContext

func NewContext() (ctx *Context)

NewContext create a new context instance.

func (*Context) Clear

func (ctx *Context) Clear()

Clear remove all values from the context.

func (*Context) Copy

func (ctx *Context) Copy() *Context

Copy create a new copy of the context.

func (*Context) Delete

func (ctx *Context) Delete(key interface{})

Delete remove a value from the context.

func (*Context) Get

func (ctx *Context) Get(key interface{}) (value interface{}, ok bool)

Get return the value matching the key from the context.

func (*Context) Set

func (ctx *Context) Set(key interface{}, val interface{})

Set add a value to the context or overrides a parent value.

func (*Context) String

func (ctx *Context) String() (str string)

String return a string representation of the context values.

type ContextHandler

type ContextHandler func(ctx *Context) http.Handler

ContextHandler is a constructor to close a Context into a http.Handler

type ContextHandlerFunc

type ContextHandlerFunc func(ctx *Context, resp http.ResponseWriter, req *http.Request)

ContextHandlerFunc is a constructor to close a Context into a http.HandlerFunc

type ContextMiddleware

type ContextMiddleware func(ctx *Context, next http.Handler) http.Handler

ContextMiddleware is a constructor to close a Context into a middleware

func Adapt

func Adapt(fn func(http.Handler) http.Handler) ContextMiddleware

Adapt add context to a middleware so it can be added to the chain.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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