kami

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2015 License: MIT Imports: 11 Imported by: 0

README

kami GoDoc Coverage

import "github.com/guregu/kami"

kami (神) is a tiny web framework using x/net/context for request context and HttpRouter for routing. It includes a simple system for running hierarchical middleware before requests, in addition to log and panic hooks. Graceful restart via einhorn is also supported.

kami is designed to be used as central registration point for your routes, middleware, and context "god object". You are encouraged to use the global functions. However, kami now supports multiple muxes with kami.New().

You are free to mount kami.Handler() wherever you wish, but a helpful kami.Serve() function is provided.

Here is a presentation about the birth of kami, explaining some of the design choices.

Example

package main

import (
	"fmt"
	"net/http"

	"github.com/guregu/kami"
	"golang.org/x/net/context"
)

func hello(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %s!", kami.Param(ctx, "name"))
}

func main() {
	kami.Get("/hello/:name", hello)
	kami.Serve()
}

Usage

  • Set up routes using kami.Get("path", handler), kami.Post(...), etc. You can use named parameters or wildcards in URLs like /hello/:name/edit or /files/*path, and access them using the context kami gives you: kami.Param(ctx, "name"). The following kinds of handlers are accepted:
    • types that implement kami.ContextHandler
    • func(context.Context, http.ResponseWriter, *http.Request)
    • types that implement http.Handler
    • func(http.ResponseWriter, *http.Request)
  • All contexts that kami uses are descended from kami.Context: this is the "god object" and the namesake of this project. By default, this is context.Background(), but feel free to replace it with a pre-initialized context suitable for your application.
  • Builds targeting Google App Engine will automatically wrap the "god object" Context with App Engine's per-request Context.
  • Add middleware with kami.Use("path", kami.Middleware). More on middleware below.
  • You can provide a panic handler by setting kami.PanicHandler. When the panic handler is called, you can access the panic error with kami.Exception(ctx).
  • You can also provide a kami.LogHandler that will wrap every request. kami.LogHandler has a different function signature, taking a WriterProxy that has access to the response status code, etc.
  • Use kami.Serve() to gracefully serve your application, or mount kami.Handler() somewhere convenient.

Middleware

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

Middleware differs from a HandlerType in that it returns a new context. You can take advantage of this to build your context by registering middleware at the approriate paths. As a special case, you may return nil to halt execution of the middleware chain.

Middleware is hierarchical. For example, a request for /hello/greg will run middleware registered under the following paths, in order:

  1. /
  2. /hello/
  3. /hello/greg

Within a path, middleware is run in the order of registration.

func init() {
	kami.Use("/", Login)
	kami.Use("/private/", LoginRequired)
}

// Login returns a new context with the appropiate user object inside
func Login(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	if u, err := user.GetByToken(ctx, r.FormValue("auth_token")); err == nil {
		ctx = user.NewContext(ctx, u)
	}
	return ctx
}

// LoginRequired stops the request if we don't have a user object
func LoginRequired(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	if _, ok := user.FromContext(ctx); !ok {
		w.WriteHeader(http.StatusForbidden)
		// ... render 503 Forbidden page
		return nil
	}
	return ctx
}	
Named parameters, wildcards, and middleware

Named parameters and wildcards in middleware are supported now. Middleware registered under a path with a wildcard will run after all hierarchical middleware.

kami.Use("/user/:id/edit", CheckAdminPermissions)
Vanilla net/http middleware

kami can use vanilla http middleware as well. kami.Use accepts functions in the form of func(next http.Handler) http.Handler. Be advised that kami will run such middleware in sequence, not in a chain. This means that standard loggers and panic handlers won't work as you expect. You should use kami.LogHandler and kami.PanicHandler instead.

The following example uses goji/httpauth to add HTTP Basic Authentication to paths under /secret/.

import (
	"github.com/goji/httpauth"
	"github.com/guregu/kami"
)

func main() {
	kami.Use("/secret/", httpauth.SimpleBasicAuth("username", "password"))
	kami.Get("/secret/message", secretMessageHandler)
	kami.Serve()
}

Independent stacks with *kami.Mux

kami was originally designed to be the "glue" between multiple packages in a complex web application. The global functions and kami.Context are an easy way for your packages to work together. However, if you would like to use kami as an embedded server within another app, serve two separate kami stacks on different ports, or otherwise would like to have an non-global version of kami, kami.New() may come in handy.

Calling kami.New() returns a fresh *kami.Mux, a completely independent kami stack. Changes to kami.Context, paths registered with kami.Get() et al, and global middleware registered with kami.Use() will not affect a *kami.Mux.

Instead, with mux := kami.New() you can change mux.Context, call mux.Use(), mux.Get(), mux.NotFound(), etc.

*kami.Mux implements http.Handler, so you may use it however you'd like!

// package admin is an admin panel web server plugin
package admin

import (
	"net/http"
	"github.com/guregu/kami"
)

// automatically mount our secret admin stuff
func init() {
	mux := kami.New()
	mux.Context = adminContext
	mux.Use("/", authorize)
	mux.Get("/admin/memstats", memoryStats)
	mux.Post("/admin/die", shutdown) // 😱
	//  ...
	http.Handle("/admin/", mux)
}

License

MIT

Acknowledgements

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// Context is the root "god object" from which every request's context will derive.
	Context = context.Background()

	// PanicHandler will, if set, be called on panics.
	// You can use kami.Exception(ctx) within the panic handler to get panic details.
	PanicHandler HandlerType
	// LogHandler will, if set, wrap every request and be called at the very end.
	LogHandler func(context.Context, mutil.WriterProxy, *http.Request)
)

Functions

func Delete

func Delete(path string, handler HandlerType)

Delete registers a DELETE handler under the given path.

func Exception

func Exception(ctx context.Context) interface{}

Exception gets the "v" in panic(v). The panic details. Only PanicHandler will receive a context you can use this with.

func Get

func Get(path string, handler HandlerType)

Get registers a GET handler under the given path.

func Handle

func Handle(method, path string, handler HandlerType)

Handle registers an arbitrary method handler under the given path.

func Handler

func Handler() http.Handler

Handler returns an http.Handler serving registered routes.

func Head(path string, handler HandlerType)

Head registers a HEAD handler under the given path.

func NotFound

func NotFound(handler HandlerType)

NotFound registers a special handler for unregistered (404) paths. If handle is nil, use the default http.NotFound behavior.

func Options

func Options(path string, handler HandlerType)

Head registers a OPTIONS handler under the given path.

func Param

func Param(ctx context.Context, name string) string

Param returns a request URL parameter, or a blank string if it doesn't exist. For example, with the path /v2/papers/:page use kami.Param(ctx, "page") to access the :page variable.

func Patch

func Patch(path string, handler HandlerType)

Patch registers a PATCH handler under the given path.

func Post

func Post(path string, handler HandlerType)

Post registers a POST handler under the given path.

func Put

func Put(path string, handler HandlerType)

Put registers a PUT handler under the given path.

func Reset

func Reset()

Reset changes the root Context to context.Background(). It removes every handler and all middleware.

func Serve

func Serve()

Serve starts kami with reasonable defaults. It works (exactly) like Goji, looking for Einhorn, the bind flag, GOJI_BIND...

func Use

func Use(path string, mw MiddlewareType)

Use registers middleware to run for the given path. Middleware with be executed hierarchically, starting with the least specific path. Middleware will be executed in order of registration. You may use wildcards in the path. Wildcard middleware will be run last, after all hierarchical middleware has run.

Adding middleware is not threadsafe.

WARNING: kami middleware is run in sequence, but standard middleware is chained; middleware that expects its code to run after the next handler, such as standard loggers and panic handlers, will not work as expected. Use kami.LogHandler and kami.PanicHandler instead. Standard middleware that does not call the next handler to stop the request is supported.

Types

type ContextHandler

type ContextHandler interface {
	ServeHTTPContext(context.Context, http.ResponseWriter, *http.Request)
}

ContextHandler is like http.Handler but supports context.

type HandlerFunc

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

HandlerFunc is like http.HandlerFunc with context.

func (HandlerFunc) ServeHTTPContext

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

type HandlerType

type HandlerType interface{}

HandlerType is the type of Handlers and types that kami internally converts to ContextHandler. In order to provide an expressive API, this type is an alias for interface{} that is named for the purposes of documentation, however only the following concrete types are accepted:

  • types that implement http.Handler
  • types that implement ContextHandler
  • func(http.ResponseWriter, *http.Request)
  • func(context.Context, http.ResponseWriter, *http.Request)

type Middleware

Middleware is a function that takes the current request context and returns a new request context. You can use middleware to build your context before your handler handles a request. As a special case, middleware that returns nil will halt middleware and handler execution (LogHandler will still run).

type MiddlewareType

type MiddlewareType interface{}

MiddlewareType represents types that kami can convert to Middleware. kami will try its best to convert standard, non-context middleware. See the Use function for important information about how kami middleware is run. The following concrete types are accepted:

  • Middleware
  • func(context.Context, http.ResponseWriter, *http.Request) context.Context
  • func(http.Handler) http.Handler [* see Use docs]
  • func(http.ContextHandler) http.ContextHandler [* see Use docs]

type Mux

type Mux struct {
	// Context is the root "god object" for this mux,
	// from which every request's context will derive.
	Context context.Context
	// PanicHandler will, if set, be called on panics.
	// You can use kami.Exception(ctx) within the panic handler to get panic details.
	PanicHandler HandlerType
	// LogHandler will, if set, wrap every request and be called at the very end.
	LogHandler func(context.Context, mutil.WriterProxy, *http.Request)
	// contains filtered or unexported fields
}

Mux is an independent kami router and middleware stack. Manipulating it is not threadsafe.

func New

func New() *Mux

New creates a new independent kami router and middleware stack. It is totally separate from the global kami.Context and middleware stack.

func (*Mux) Delete

func (m *Mux) Delete(path string, handler HandlerType)

Delete registers a DELETE handler under the given path.

func (*Mux) Get

func (m *Mux) Get(path string, handler HandlerType)

Get registers a GET handler under the given path.

func (*Mux) Handle

func (m *Mux) Handle(method, path string, handler HandlerType)

Handle registers an arbitrary method handler under the given path.

func (*Mux) Head

func (m *Mux) Head(path string, handler HandlerType)

Head registers a HEAD handler under the given path.

func (*Mux) NotFound

func (m *Mux) NotFound(handler HandlerType)

NotFound registers a special handler for unregistered (404) paths. If handle is nil, use the default http.NotFound behavior.

func (*Mux) Options

func (m *Mux) Options(path string, handler HandlerType)

Head registers a OPTIONS handler under the given path.

func (*Mux) Patch

func (m *Mux) Patch(path string, handler HandlerType)

Patch registers a PATCH handler under the given path.

func (*Mux) Post

func (m *Mux) Post(path string, handler HandlerType)

Post registers a POST handler under the given path.

func (*Mux) Put

func (m *Mux) Put(path string, handler HandlerType)

Put registers a PUT handler under the given path.

func (*Mux) ServeHTTP

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

ServeHTTP handles an HTTP request, running middleware and forwarding the request to the appropriate handler. Implements the http.Handler interface for easy composition with other frameworks.

func (Mux) Use

func (m Mux) Use(path string, mw MiddlewareType)

Use registers middleware to run for the given path. See the global Use function's documents for information on how middleware works.

Jump to

Keyboard shortcuts

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