handy

package
Version: v0.0.0-...-73d3301 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2016 License: GPL-2.0 Imports: 10 Imported by: 0

README

Handy

Handy is a fast and simple HTTP multiplexer for Golang. It fills some gaps related to the default Golang's HTTP multiplexer:

* URI variable support (eg: "/test/{foo}")
* Codecs
* Interceptors

Handy uses the Handler As The State Of the Request. This approach allows simple and advanced usages.

Creating a Handler

You just need to embed handy.DefaultHandler in your structure and override the HTTP method:

package main

import (
	"handy"
	"log"
	"net/http"
)

func main() {
	srv := handy.NewHandy()
	srv.Handle("/hello/", func() handy.Handler { return new(MyHandler) })
	log.Fatal(http.ListenAndServe(":8080", srv))
}

type MyHandler struct {
	handy.DefaultHandler
}

func (h *MyHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
}

Path with variables

Path variables must be enclosed by braces.

srv.Handle("/hello/{name}", func() handy.Handler { 
	return new(MyHandler) 
})

And you can read them using the Handler's fields. You just need to tag the field.

type MyHandler struct {
	handy.DefaultHandler
	Name string `param:"name"`
}

func (h *MyHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello " + h.Name))
}
URI variables - a complete example:
package main

import (
	"handy"
	"log"
	"net/http"
)

func main() {
	srv := handy.NewHandy()
	srv.Handle("/hello/{name}", func() handy.Handler {
		return new(MyHandler)
	})
	log.Fatal(http.ListenAndServe(":8080", srv))
}

type MyHandler struct {
	handy.DefaultHandler
	Name string `param:"name"`
}

func (h *MyHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello " + h.Name))
}

Interceptors

To execute functions before and/or after the verb method being called you can use interceptors. To do so you need to create a InterceptorChain in you Handler to be executed Before or After the HTTP verb method.

Interceptors - a complete example

package main

import (
	"handy"
	"log"
	"net/http"
)

func main() {
	srv := handy.NewHandy()
	srv.Handle("/hello/", func() handy.Handler {
		return new(MyHandler)
	})
	log.Fatal(http.ListenAndServe(":8080", srv))
}

type MyHandler struct {
	handy.DefaultHandler
}

func (h *MyHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Success"))
}

func (h *MyHandler) Interceptors() handy.InterceptorChain {
	return handy.NewInterceptorChain().Chain(new(TimerInterceptor))
}

type TimerInterceptor struct {
	Timer time.Time
	handy.NoErrorInterceptor
}

func (i *TimerInterceptor) Before(w http.ResponseWriter, r *http.Request) {
	i.Timer = time.Now()
	return nil
}

func (i *TimerInterceptor) After(w http.ResponseWriter, r *http.Request) {
	handy.Logger.Println("Took", time.Since(i.Timer))
}

Codec interceptor

Handy comes with a JSONCodec interceptor. It can be used to automatically unmarshal requests and marshal responses. It does so by reading special tags in your handler:

type MyResponse struct {
	Message string `json:"message"`
}

type MyHandler struct {
	handy.DefaultHandler
	// this structure will be used only for get and put methods
	Response MyResponse `response:"get,put"` 
}

Now, you just need to include JSONCode in the handler's interceptor chain:

func (h *MyHandler) Interceptors() handy.InterceptorChain {
	codec := inteceptor.JSONCodec{}
	codec.SetStruct(h)
	return handy.NewInterceptorChain().Chain(codec)
}
Codec inteceptor - a complete example:
package main

import (
    "handy"
    "handy/interceptor"
    "log"
    "net/http"
)

func main() {
    srv := handy.NewHandy()
    srv.Handle("/hello/", func() handy.Handler {
        return new(MyHandler)
    })
    log.Fatal(http.ListenAndServe(":8080", srv))
}

type MyHandler struct {
    handy.DefaultHandler
    Response MyResponse `response:"all"`
}

func (h *MyHandler) Get(w http.ResponseWriter, r *http.Request) {
    h.Response.Message = "hello world"
}

func (h *MyHandler) Interceptors() handy.InterceptorChain {
	codec := inteceptor.JSONCodec{}
	codec.SetStruct(h)
	return handy.NewInterceptorChain().Chain(codec)
}

type MyResponse struct {
    Message string `json:"message"`
}

Tests

You can use [Go's httptest package] (http://golang.org/pkg/net/http/httptest/)

package handler

import (
	"handy"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestHandler(t *testing.T) {
	mux := handy.NewHandy()
	h := new(HelloHandler)
	mux.Handle("/{name}/{id}", func() handy.Handler {
		return h
	})

	req, err := http.NewRequest("GET", "/foo/10", nil)
	if err != nil {
		t.Fatal(err)
	}

	w := httptest.NewRecorder()
	mux.ServeHTTP(w, req)

	if h.Id != 10 {
		t.Errorf("Unexpected Id value %d", h.Id)
	}

	if h.Name != "foo" {
		t.Errorf("Unexpected Name value %s", h.Name)
	}

	t.Logf("%d - %s - %d", w.Code, w.Body.String(), h.Id)
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrRouteNotFound      = errors.New("Router not found")
	ErrRouteAlreadyExists = errors.New("Route already exists")
	ErrCannotAppendRoute  = errors.New("Cannot append route")
	ErrOnlyOneWildcard    = errors.New("Only one wildcard is allowed in this level")
)
View Source
var (
	Logger *log.Logger
)

Functions

This section is empty.

Types

type AfterInterceptorFunc

type AfterInterceptorFunc func(w http.ResponseWriter, r *http.Request)

func (AfterInterceptorFunc) After

func (AfterInterceptorFunc) Before

type BeforeInterceptorFunc

type BeforeInterceptorFunc func(w http.ResponseWriter, r *http.Request)

func (BeforeInterceptorFunc) After

func (BeforeInterceptorFunc) Before

type BufferedResponseWriter

type BufferedResponseWriter struct {
	Body *bytes.Buffer
	// contains filtered or unexported fields
}

func NewBufferedResponseWriter

func NewBufferedResponseWriter(w http.ResponseWriter) *BufferedResponseWriter

func (*BufferedResponseWriter) Flush

func (rw *BufferedResponseWriter) Flush()

func (*BufferedResponseWriter) Header

func (rw *BufferedResponseWriter) Header() http.Header

Header returns the response headers.

func (*BufferedResponseWriter) Status

func (rw *BufferedResponseWriter) Status() int

func (*BufferedResponseWriter) Write

func (rw *BufferedResponseWriter) Write(buf []byte) (int, error)

Write always succeeds and writes to rw.Body, if not nil.

func (*BufferedResponseWriter) WriteHeader

func (rw *BufferedResponseWriter) WriteHeader(code int)

type DefaultHandler

type DefaultHandler struct {
	http.Handler
	NopInterceptorChain
}

func (*DefaultHandler) Delete

func (s *DefaultHandler) Delete(w http.ResponseWriter, r *http.Request)

func (*DefaultHandler) Get

func (*DefaultHandler) Head

func (*DefaultHandler) Patch

func (s *DefaultHandler) Patch(w http.ResponseWriter, r *http.Request)

func (*DefaultHandler) Post

func (*DefaultHandler) Put

type Handy

type Handy struct {
	CountClients bool
	Recover      func(interface{})
	// contains filtered or unexported fields
}

func NewHandy

func NewHandy() *Handy

func (*Handy) Handle

func (handy *Handy) Handle(pattern string, h HandyFunc)

func (*Handy) ServeHTTP

func (handy *Handy) ServeHTTP(rw http.ResponseWriter, r *http.Request)

type HandyFunc

type HandyFunc func() Handler

type Interceptor

type Interceptor interface {
	Before(w http.ResponseWriter, r *http.Request)
	After(w http.ResponseWriter, r *http.Request)
}

type InterceptorChain

type InterceptorChain []Interceptor

func NewInterceptorChain

func NewInterceptorChain() InterceptorChain

func (InterceptorChain) Chain

type NopInterceptorChain

type NopInterceptorChain struct{}

func (*NopInterceptorChain) Interceptors

func (n *NopInterceptorChain) Interceptors() InterceptorChain

type RouteMatch

type RouteMatch struct {
	URIVars map[string]string
	Handler HandyFunc
}

type Router

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

func NewRouter

func NewRouter() *Router

func (*Router) AppendRoute

func (r *Router) AppendRoute(uri string, h HandyFunc) error

func (*Router) Match

func (r *Router) Match(uri string) (*RouteMatch, error)

This method rebuilds a route based on a given URI

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL