r2router

package module
v0.0.0-...-1023140 Latest Latest
Warning

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

Go to latest
Published: May 23, 2015 License: MIT Imports: 9 Imported by: 38

README

r2router

A simple router which supports named parameter. Idea for API or backend without any static content. This package contains 2 router types. Router is for pure routing and Seefor supports middleware stacks.

Middlewares are divided into 2 groups, one runs before routing and one runs after routing. Before middleware is thought for serving static, logging, recovery from panic and so on. After middleware is thought for pre-processing data before executing endpoint handler. One can do this by using AppSet method on Params. This mean that Before middlewares are always executed, except when a middleware cancels and does not call next(), meanwhile After middlewares are only call if a route is hit. Each After middleware also has a chance to response and stop calling next().

By default there is no middleware added. See list bellow for suitable middlewares, including recovery which has been moved from this repos.

One interesting feature this package has is measurement of endpoint performance. The timer measures how long time it takes average for each route. The timer itself is a http.Handler so one can use it to serve these statistics locally (see example/timer.go).

There is also a route manager for registering all your routes. This can use for setting up endpoints and also use for building urls.

GoDoc Build Status Go Walker

Example

Router

package main

import (
	"github.com/vanng822/r2router"
	"net/http"
)

func main() {
	router := r2router.NewRouter()
	router.Get("/users/:user", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		w.Write([]byte(p.Get("user")))
	})
	http.ListenAndServe(":8080", router)
}

Demo: http://premailer.isgoodness.com/

If your routes don't contain named params and you have existing http.HandlerFunc then you can wrap as bellow


package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"net/http"
)

// Wrapper for http.HandlerFunc, similar can be done for http.Handler
func RouteHandlerFunc(next http.HandlerFunc) r2router.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, _ r2router.Params) {
		next(w, r)
	}
}

func main() {
	seefor := r2router.NewSeeforRouter()
	seefor.Get("/hello/world", RouteHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello world!")
	}))
	http.ListenAndServe("127.0.0.1:8080", seefor)
}

Measuring endpoint performance using Timer

package main

import (
	"github.com/vanng822/r2router"
	"net/http"
)

func main() {
	router := r2router.NewSeeforRouter()
	router.Group("/hello", func(r *r2router.GroupRouter) {
		r.Get("/kitty", func(w http.ResponseWriter, r *http.Request, _ r2router.Params) {
			w.Write([]byte("Mau"))
		})

		r.Get("/duck", func(w http.ResponseWriter, r *http.Request, _ r2router.Params) {
			w.Write([]byte("Crispy"))
		})

		r.Get("/:name", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
			w.Write([]byte(p.Get("name")))
		})
	})
	timer := router.UseTimer(nil)
	
	go http.ListenAndServe("127.0.0.1:8080", router)
	http.ListenAndServe("127.0.0.1:8081", timer)
}

Demo: http://premailer.isgoodness.com/timers

Middleware

package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"net/http"
	"log"
	"time"
)

func main() {
	seefor := r2router.NewSeeforRouter()
	// measure time middleware
	seefor.Before(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now()
			next.ServeHTTP(w, r)
			log.Printf("took: %s", time.Now().Sub(start))
		})
	})
	// set label "say"
	seefor.After(func(next r2router.Handler) r2router.Handler {
		return r2router.HandlerFunc(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
			p.AppSet("say", "Hello")
			next.ServeHTTP(w, r, p)
		})
	})

	seefor.After(r2router.Wrap(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		p.AppSet("goodbye", "Bye bye")
	}))

	seefor.Get("/hello/:name", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		fmt.Fprintf(w, "%s %s!\n%s", p.AppGet("say").(string), p.Get("name"), p.AppGet("goodbye"))
	})
	
	http.ListenAndServe(":8080", seefor)
}

If you want to add middlewares for a specific route then you should create your own wrapper. This way you will have full control over your middlewares. You could do something like bellow


package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"net/http"
)

// Your own route middle wrapper
func RouteMiddleware(next r2router.HandlerFunc) r2router.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		p.AppSet("say", "Hello")
		next(w, r, p)
	}
}

func main() {
	seefor := r2router.NewSeeforRouter()
	seefor.Get("/hello/:name", RouteMiddleware(func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		fmt.Fprintf(w, "%s %s!", p.AppGet("say").(string), p.Get("name"))
	}))
	http.ListenAndServe("127.0.0.1:8080", seefor)
}

Route manager

package main

import (
	"fmt"
	"github.com/vanng822/r2router"
	"net/http"
)

func main() {
	seefor := r2router.NewSeeforRouter()
	rmanager := r2router.NewRouteManager()
	// register and use it at the same time
	seefor.Get(rmanager.Add("hello::name", "/hello/:name"), func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		fmt.Fprintf(w, "Hello %s!", p.Get("name"))
	})

	// Or register first elsewhere and get it later
	rmanager.Add("redirect::name", "/redirect/:name")

	seefor.Get(rmanager.PathFor("redirect::name"), func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		// Building url for routename "hello::name" and redirect
		http.Redirect(w, r, rmanager.UrlFor("hello::name", r2router.P{"name": []string{p.Get("name")}}), http.StatusFound)
	})

	http.ListenAndServe("127.0.0.1:8080", seefor)
}

Middlewares

Panic recovery https://github.com/vanng822/recovery

Access log https://github.com/vanng822/accesslog

Basic auth https://github.com/goji/httpauth

CORS https://github.com/rs/cors

Quick security wins https://github.com/unrolled/secure

HMAC authentication https://github.com/auroratechnologies/vangoh

Static content https://github.com/hypebeast/gojistatic

Generic packages

Rendering https://github.com/unrolled/render

Session https://github.com/gorilla/sessions

HTTP request data binding & validation https://github.com/mholt/binding

Documentation

Overview

Package r2router provide a simple router. Suitable for API where you could map parameter from the url.

package main

import (
	"github.com/vanng822/r2router"
	"net/http"
)

func main() {
	router := r2router.NewRouter()
	router.Get("/users/:user", func(w http.ResponseWriter, r *http.Request, p r2router.Params) {
		w.Write([]byte(p.Get("user")))
	})
	http.ListenAndServe(":8080", router)
}

Index

Constants

View Source
const (
	HTTP_METHOD_GET     = "GET"
	HTTP_METHOD_POST    = "POST"
	HTTP_METHOD_DELETE  = "DELETE"
	HTTP_METHOD_OPTIONS = "OPTIONS"
	HTTP_METHOD_HEAD    = "HEAD"
	HTTP_METHOD_PUT     = "PUT"
	HTTP_METHOD_PATCH   = "PATCH"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type After

type After func(next Handler) Handler

After defines how a middleware should look like. After middlewares are for handling request after routing. After middlewares are executed in the order they were inserted. A middleware can choose to response to a request and not call next handler

func Wrap

func Wrap(handler HandlerFunc) After

Wrap for wrapping a handler to After middleware Be aware that it will not be able to stop execution propagation That is it will continue to execute the next middleware/handler

func WrapHandler

func WrapHandler(handler http.Handler) After

WrapHandler for wrapping a http.Handler to After middleware. Be aware that it will not be able to stop execution propagation That is it will continue to execute the next middleware/handler

type Before

type Before func(next http.Handler) http.Handler

Before defines middleware interface. Before middlewares are for handling request before routing. Before middlewares are executed in the order they were inserted. A middleware can choose to response to a request and not call next handler

func WrapBeforeHandler

func WrapBeforeHandler(handler http.Handler) Before

WrapBeforeHandler for wrapping a http.Handler to Before middleware. Be aware that it will not be able to stop execution propagation That is it will continue to execute the next middleware/handler

type Counter

type Counter struct {
	Count     int64         // Number of requests at this endpoint
	Tot       time.Duration // Accumulated total time
	Max       time.Duration // Worst time of all requests
	Min       time.Duration // Best time of all requests
	BeforeTot time.Duration // Total time of Before middlewares
	AfterTot  time.Duration // Total time of After middlewares
}

Statistic entry for one endpoint. Values are atomic updated

func (*Counter) Accumulate

func (c *Counter) Accumulate(started, beforeEnd, after, end time.Time)

type GroupRouter

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

GroupRouter is a helper for grouping endpoints All methods are proxy of Router Suitable for grouping different methods of an endpoint

func NewGroupRouter

func NewGroupRouter(r *Router, path string) *GroupRouter

NewGroupRouter return GroupRouter which is a helper to construct a group of endpoints, such example could be API-version or different methods for an endpoint You should always use router.Group instead of using this directly

func (*GroupRouter) Delete

func (gr *GroupRouter) Delete(path string, handler HandlerFunc)

func (*GroupRouter) Get

func (gr *GroupRouter) Get(path string, handler HandlerFunc)

func (*GroupRouter) Head

func (gr *GroupRouter) Head(path string, handler HandlerFunc)

func (*GroupRouter) Patch

func (gr *GroupRouter) Patch(path string, handler HandlerFunc)

func (*GroupRouter) Post

func (gr *GroupRouter) Post(path string, handler HandlerFunc)

func (*GroupRouter) Put

func (gr *GroupRouter) Put(path string, handler HandlerFunc)

type Handler

type Handler interface {
	ServeHTTP(w http.ResponseWriter, req *http.Request, params Params)
}

type HandlerFunc

type HandlerFunc func(w http.ResponseWriter, req *http.Request, params Params)

HandlerFunc define interface handler

func (HandlerFunc) ServeHTTP

func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, req *http.Request, params Params)

type M

type M map[string]interface{}

Shortcut for map[string]interface{} Helpful to build data for json response

type P

type P map[string][]string

Shortcut for map[string][]string

type Params

type Params interface {
	// Get returns param value for the given key
	Get(key string) string
	// Has is for checking if parameter value exists
	// for given key
	Has(key string) bool
	// AppSet is for application to set own data
	AppSet(key interface{}, val interface{})
	// AppGet returns the value from AppSet
	AppGet(key interface{}) interface{}
	// AppHas is for checking if the application
	// has set data for given key
	AppHas(key interface{}) bool
}

Params is for parameters that are matched from URL. It is also brigde to forward data from middleware. An Example could be that a middleware to identify the user API key, verify and get user data

type RouteManager

type RouteManager interface {
	// For setting baseurl if one needs full url
	SetBaseUrl(baseUrl string)
	// Register a route and return the path
	// This can be good for adding and register handler at the same time
	// router.Get(rm.Add("user", "/user/:id"), handler)
	Add(routeName, path string) string
	// Return the path for a specific route name
	// Use for register handler
	// router.Delete(rm.PathFor("user"), handler)
	PathFor(routeName string) string
	// Returning url for given route name and provided data
	// Will panic if missmatched
	UrlFor(routeName string, params map[string][]string) string
	// Returning url for given path and provided data
	// Will panic if missmatched
	UrlForPath(path string, params map[string][]string) string
}

For managing route and getting url

func NewRouteManager

func NewRouteManager() RouteManager

type Router

type Router struct {
	HandleMethodNotAllowed bool
	MethodNotAllowed       http.HandlerFunc
	NotFound               http.HandlerFunc
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter() *Router

NewRouter return a new Router

func (*Router) AddHandler

func (r *Router) AddHandler(method, path string, handler HandlerFunc)

func (*Router) Delete

func (r *Router) Delete(path string, handler HandlerFunc)

func (*Router) Dump

func (r *Router) Dump() string

func (*Router) Get

func (r *Router) Get(path string, handler HandlerFunc)

func (*Router) Group

func (r *Router) Group(path string, fn func(r *GroupRouter))

Group takes a path which typically a prefix for an endpoint It will call callback function with a group router which you can add handler for different request methods

func (*Router) Head

func (r *Router) Head(path string, handler HandlerFunc)

func (*Router) Patch

func (r *Router) Patch(path string, handler HandlerFunc)

func (*Router) Post

func (r *Router) Post(path string, handler HandlerFunc)

func (*Router) Put

func (r *Router) Put(path string, handler HandlerFunc)

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

http Handler Interface

type Seefor

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

Seefor is a subtype of Router. It supports a simple middleware layers. Middlewares are always executed before handler, no matter where or when they are added. And middlewares are executed in the order they were inserted.

func NewSeeforRouter

func NewSeeforRouter() *Seefor

NewSeeforRouter for creating a new instance of Seefor router

func (*Seefor) After

func (c4 *Seefor) After(middleware ...After)

After is for adding middleware for running after routing

func (*Seefor) Before

func (c4 *Seefor) Before(middleware ...Before)

Before is for adding middleware for running before routing

func (*Seefor) ServeHTTP

func (c4 *Seefor) ServeHTTP(w http.ResponseWriter, req *http.Request)

Implementing http handler interface. This is a override of Router.ServeHTTP for handling middlewares

func (*Seefor) UseTimer

func (c4 *Seefor) UseTimer(timer *Timer) *Timer

UseTimer set timer for meaturing endpoint performance. If timer is nil and no timer exists then a new timer will be created else existing timer will be returned. You can serve statistics internal using Timer as handler

type Stat

type Stat struct {
	Route     string        `json:"route"`
	Count     int64         `json:"count"`
	Tot       time.Duration `json:"tot"`
	Max       time.Duration `json:"max"`
	Min       time.Duration `json:"min"`
	Avg       time.Duration `json:"avg"`
	AvgBefore time.Duration `json:"avg_before"`
	AvgAfter  time.Duration `json:"avg_after"`
}

Tot, Max, Min and Avg is time.Duration which mean in nanoseconds

type Stats

type Stats struct {
	Generated time.Time `json:"generated"`
	UpTime    string    `json:"upTime"`
	Result    []*Stat   `json:"result"`
	SortBy    string    `json:"sortBy"`
}

For generate statistics

func (*Stats) Len

func (s *Stats) Len() int

Implements sort interface

func (*Stats) Less

func (s *Stats) Less(i, j int) bool

func (*Stats) Swap

func (s *Stats) Swap(i, j int)

type Timer

type Timer struct {
	Since time.Time
	// contains filtered or unexported fields
}

Keep track on routes performance. Each route has own Counter entry

func NewTimer

func NewTimer() *Timer

func (*Timer) Get

func (t *Timer) Get(name string) *Counter

Get returns a Counter for a route. If there is no entry it will create a new one. It will lock during creation

func (*Timer) ServeHTTP

func (t *Timer) ServeHTTP(w http.ResponseWriter, req *http.Request)

For serving statistics

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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