chi

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Mar 31, 2016 License: MIT Imports: 6 Imported by: 8,090

README

chi

GoDoc Widget Travis Widget

chi is a small, fast and expressive router / mux for Go HTTP services built with net/context.

Chi encourages writing services by composing small handlers and middlewares with many or few routes. Each middleware is like a layer of an onion connected through a consistent interface (http.Handler or chi.Handler) and a context.Context argument that flows down the layers during a request's lifecycle.

In order to get the most out of this pattern, chi's routing methods (Get, Post, Handle, Mount, etc.) support inline middlewares, middleware groups, and mounting (composing) any chi router to another - a bushel of onions. We've designed the Pressly API (150+ routes/handlers) exactly like this and its scaled very well.

alt tag

Features

  • Lightweight - cloc'd in <1000 LOC for the chi router
  • Fast - yes, see benchmarks
  • Zero allocations - no GC pressure during routing
  • Designed for modular/composable APIs - middlewares, inline middleware groups/chains, and subrouter mounting
  • Context control - built on net/context with value chaining, deadlines and timeouts
  • Robust - tested / used in production

Router design

Chi's router is based on a kind of Patricia Radix trie. Built on top of the tree is the Router interface:

// Register a middleware handler (or few) on the middleware stack
Use(middlewares ...interface{})

// Register a new middleware stack
Group(fn func(r Router)) Router

// Mount an inline sub-router
Route(pattern string, fn func(r Router)) Router

// Mount a sub-router
Mount(pattern string, handlers ...interface{})

// Register routing handler for all http methods
Handle(pattern string, handlers ...interface{})

// Register routing handler for CONNECT http method
Connect(pattern string, handlers ...interface{})

// Register routing handler for HEAD http method
Head(pattern string, handlers ...interface{})

// Register routing handler for GET http method
Get(pattern string, handlers ...interface{})

// Register routing handler for POST http method
Post(pattern string, handlers ...interface{})

// Register routing handler for PUT http method
Put(pattern string, handlers ...interface{})

// Register routing handler for PATCH http method
Patch(pattern string, handlers ...interface{})

// Register routing handler for DELETE http method
Delete(pattern string, handlers ...interface{})

// Register routing handler for TRACE http method
Trace(pattern string, handlers ...interface{})

// Register routing handler for OPTIONS http method
Options(pattern string, handlers ...interface{})

Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/:userID) and wildcards (ie. /admin/*).

The handlers argument can be a single request handler, or a chain of middleware handlers, followed by a request handler. The request handler is required, and must be the last argument.

We lose type checking of the handlers, but that'll be resolved sometime in the future, we hope, when Go's stdlib supports net/context in net/http. For now, chi checks the types at runtime and panics in case of a mismatch.

The supported handlers are as follows..

Middleware handlers
// Standard HTTP middleware. Compatible and friendly for when a request context isn't needed.
func StdMiddleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    next.ServeHTTP(w, r)
  })
}
// net/context HTTP middleware. Useful for signaling to stop processing, adding a timeout,
// cancellation, or passing data down the middleware chain.
func CtxMiddleware(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    ctx = context.WithValue(ctx, "key", "value")
    next.ServeHTTPC(ctx, w, r)
  })
}
Request handlers
// Standard HTTP handler
func StdHandler(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("hi"))
}
// net/context HTTP request handler
func CtxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  userID := chi.URLParam(ctx, "userID") // from a route like /users/:userID
  key := ctx.Value("key").(string)
  w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
}

net/context?

net/context is a tiny library written by Sameer Ajmani that provides a simple interface to signal context across call stacks and goroutines.

Learn more at https://blog.golang.org/context

and..

Examples

Examples:

  • simple - The power of handler composability
  • rest - REST apis made easy; includes a simple JSON responder

Preview:

import (
  //...
  "github.com/pressly/chi"
  "github.com/pressly/chi/middleware"
  "golang.org/x/net/context"
)

func main() {
  r := chi.NewRouter()

  // A good base middleware stack
  r.Use(middleware.RequestID)
  r.Use(middleware.RealIP)
  r.Use(middleware.Logger)
  r.Use(middleware.Recoverer)

  // When a client closes their connection midway through a request, the
  // http.CloseNotifier will cancel the request context (ctx).
  r.Use(middleware.CloseNotify)

  // Set a timeout value on the request context (ctx), that will signal
  // through ctx.Done() that the request has timed out and further
  // processing should be stopped.
  r.Use(middleware.Timeout(60 * time.Second))

  r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hi"))
  })

  // RESTy routes for "articles" resource
  r.Route("/articles", func(r chi.Router) {
    r.Get("/", paginate, listArticles)  // GET /articles
    r.Post("/", createArticle)          // POST /articles

    r.Route("/:articleID", func(r chi.Router) {
      r.Use(ArticleCtx)
      r.Get("/", getArticle)            // GET /articles/123
      r.Put("/", updateArticle)         // PUT /articles/123
      r.Delete("/", deleteArticle)      // DELETE /articles/123
    })
  })

  // Mount the admin sub-router
  r.Mount("/admin", adminRouter())

  http.ListenAndServe(":3333", r)
}

func ArticleCtx(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    articleID := chi.URLParam(ctx, "articleID")
    article, err := dbGetArticle(articleID)
    if err != nil {
      http.Error(w, http.StatusText(404), 404)
      return
    }
    ctx = context.WithValue(ctx, "article", article)
    next.ServeHTTPC(ctx, w, r)
  })
}

func getArticle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  article, ok := ctx.Value("article").(*Article)
  if !ok {
    http.Error(w, http.StatusText(422), 422)
    return
  }
  w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

// A completely separate router for administrator routes
func adminRouter() chi.Router {
  r := chi.NewRouter()
  r.Use(AdminOnly)
  r.Get("/", adminIndex)
  r.Get("/accounts", adminListAccounts)
  return r
}

func AdminOnly(next chi.Handler) chi.Handler {
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    perm, ok := ctx.Value("acl.permission").(YourPermissionType)
    if !ok || !perm.IsAdmin() {
      http.Error(w, http.StatusText(403), 403)
      return
    }
    next.ServeHTTPC(ctx, w, r)
  })
}

Middlewares

Chi comes equipped with an optional middleware package, providing:


Middleware Description
RequestID Injects a request ID into the context of each request.
RealIP Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP.
Logger Logs the start and end of each request with the elapsed processing time.
Recoverer Gracefully absorb panics and prints the stack trace.
NoCache Sets response headers to prevent clients from caching.
CloseNotify Signals to the request context when a client has closed their connection.
Timeout Signals to the request context when the timeout deadline is reached.
Throttle Puts a ceiling on the number of concurrent requests.

Other middlewares:

please submit a PR if you'd like to include a link to a chi middleware

Future

We're hoping that by Go 1.7 (in 2016), net/context will be in the Go stdlib and net/http will support context.Context. You'll notice that chi.Handler and http.Handler are very similar and the middleware signatures follow the same structure. One day chi.Handler will be deprecated and the router will live on just as it is without any dependencies beyond stdlib. And... then, we have infinitely more middlewares to compose from the community!!

See discussions:

Benchmarks

The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark

BenchmarkChi_Param       	10000000	       181 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_Param5      	 3000000	       570 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_Param20     	 1000000	      2057 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_ParamWrite  	 5000000	       245 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GithubStatic	 5000000	       250 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GithubParam 	 2000000	       589 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GithubAll   	   10000	    102664 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GPlusStatic 	10000000	       161 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GPlusParam  	 5000000	       291 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GPlus2Params	 5000000	       393 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_GPlusAll    	  300000	      4335 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_ParseStatic 	10000000	       162 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_ParseParam  	10000000	       227 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_Parse2Params	 5000000	       327 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_ParseAll    	  200000	      7368 ns/op	       0 B/op	       0 allocs/op
BenchmarkChi_StaticAll   	   30000	     57990 ns/op	       0 B/op	       0 allocs/op

Credits

We'll be more than happy to see your contributions!

License

Copyright (c) 2015-2016 Peter Kieltyka

Licensed under MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func URLParam

func URLParam(ctx context.Context, key string) string

Types

type Context

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

func RootContext

func RootContext(ctx context.Context) *Context

Returns the root level chi Context object

func (*Context) Param

func (x *Context) Param(key string) string

type Handler

type Handler interface {
	ServeHTTPC(context.Context, http.ResponseWriter, *http.Request)
}

Handler is like net/http's http.Handler, but also includes a mechanism for serving requests with a context.

type HandlerFunc

type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request)

HandlerFunc is like net/http's http.HandlerFunc, but supports a context object.

func (HandlerFunc) ServeHTTP

func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP provides compatibility with http.Handler.

func (HandlerFunc) ServeHTTPC

func (h HandlerFunc) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

ServeHTTPC wraps ServeHTTP with a context parameter.

type Mux

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

func NewMux

func NewMux() *Mux

func NewRouter

func NewRouter() *Mux

func (*Mux) Connect

func (mx *Mux) Connect(pattern string, handlers ...interface{})

func (*Mux) Delete

func (mx *Mux) Delete(pattern string, handlers ...interface{})

func (*Mux) Get

func (mx *Mux) Get(pattern string, handlers ...interface{})

func (*Mux) Group

func (mx *Mux) Group(fn func(r Router)) Router

func (*Mux) Handle

func (mx *Mux) Handle(pattern string, handlers ...interface{})

func (*Mux) Head

func (mx *Mux) Head(pattern string, handlers ...interface{})

func (*Mux) Mount

func (mx *Mux) Mount(path string, handlers ...interface{})

func (*Mux) NotFound

func (mx *Mux) NotFound(h HandlerFunc)

NotFound sets a custom handler for the case when no routes match

func (*Mux) Options

func (mx *Mux) Options(pattern string, handlers ...interface{})

func (*Mux) Patch

func (mx *Mux) Patch(pattern string, handlers ...interface{})

func (*Mux) Post

func (mx *Mux) Post(pattern string, handlers ...interface{})

func (*Mux) Put

func (mx *Mux) Put(pattern string, handlers ...interface{})

func (*Mux) Route

func (mx *Mux) Route(pattern string, fn func(r Router)) Router

func (*Mux) ServeHTTP

func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Mux) ServeHTTPC

func (mx *Mux) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request)

func (*Mux) Trace

func (mx *Mux) Trace(pattern string, handlers ...interface{})

func (*Mux) Use

func (mx *Mux) Use(mws ...interface{})

Append to the middleware stack

type Router

type Router interface {
	http.Handler
	Handler

	Use(middlewares ...interface{})
	Group(fn func(r Router)) Router
	Route(pattern string, fn func(r Router)) Router
	Mount(pattern string, handlers ...interface{})

	Handle(pattern string, handlers ...interface{})
	NotFound(h HandlerFunc)

	Connect(pattern string, handlers ...interface{})
	Head(pattern string, handlers ...interface{})
	Get(pattern string, handlers ...interface{})
	Post(pattern string, handlers ...interface{})
	Put(pattern string, handlers ...interface{})
	Patch(pattern string, handlers ...interface{})
	Delete(pattern string, handlers ...interface{})
	Trace(pattern string, handlers ...interface{})
	Options(pattern string, handlers ...interface{})
}

type WalkFn

type WalkFn func(path string, handler Handler) bool

WalkFn is used when walking the tree. Takes a key and value, returning if iteration should be terminated.

Directories

Path Synopsis
_examples

Jump to

Keyboard shortcuts

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