negroni

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2018 License: MIT Imports: 13 Imported by: 2,394

README

Negroni

GoDoc Build Status codebeat codecov

Notice: This is the library formerly known as github.com/codegangsta/negroni -- Github will automatically redirect requests to this repository, but we recommend updating your references for clarity.

Negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.

If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.

Language Translations:

Getting Started

After installing Go and setting up your GOPATH, create your first .go file. We'll call it server.go.

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic() // Includes some default middlewares
  n.UseHandler(mux)

  http.ListenAndServe(":3000", n)
}

Then install the Negroni package (NOTE: >= go 1.1 is required):

go get github.com/urfave/negroni

Then run your server:

go run server.go

You will now have a Go net/http webserver running on localhost:3000.

Packaging

If you are on Debian, negroni is also available as a package that you can install via apt install golang-github-urfave-negroni-dev (at the time of writing, it is in the sid repositories).

Is Negroni a Framework?

Negroni is not a framework. It is a middleware-focused library that is designed to work directly with net/http.

Routing?

Negroni is BYOR (Bring your own Router). The Go community already has a number of great http routers available, and Negroni tries to play well with all of them by fully supporting net/http. For instance, integrating with Gorilla Mux looks like so:

router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)

n := negroni.New(Middleware1, Middleware2)
// Or use a middleware with the Use() function
n.Use(Middleware3)
// router goes last
n.UseHandler(router)

http.ListenAndServe(":3001", n)

negroni.Classic()

negroni.Classic() provides some default middleware that is useful for most applications:

This makes it really easy to get started with some useful features from Negroni.

Handlers

Negroni provides a bidirectional middleware flow. This is done through the negroni.Handler interface:

type Handler interface {
  ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

If a middleware hasn't already written to the ResponseWriter, it should call the next http.HandlerFunc in the chain to yield to the next middleware handler. This can be used for great good:

func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  // do some stuff before
  next(rw, r)
  // do some stuff after
}

And you can map it to the handler chain with the Use function:

n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))

You can also map plain old http.Handlers:

n := negroni.New()

mux := http.NewServeMux()
// map your routes

n.UseHandler(mux)

http.ListenAndServe(":3000", n)

With()

Negroni has a convenience function called With. With takes one or more Handler instances and returns a new Negroni with the combination of the receiver's handlers and the new handlers.

// middleware we want to reuse
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)

// `specific` is a new negroni with the handlers from `common` combined with the
// the handlers passed in
specific := common.With(
	SpecificMiddleware1,
	SpecificMiddleware2
)

Run()

Negroni has a convenience function called Run. Run takes an addr string identical to http.ListenAndServe.

package main

import (
  "github.com/urfave/negroni"
)

func main() {
  n := negroni.Classic()
  n.Run(":8080")
}

If no address is provided, the PORT environment variable is used instead. If the PORT environment variable is not defined, the default address will be used. See Run for a complete description.

In general, you will want to use net/http methods and pass negroni as a Handler, as this is more flexible, e.g.:

package main

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

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic() // Includes some default middlewares
  n.UseHandler(mux)

  s := &http.Server{
    Addr:           ":8080",
    Handler:        n,
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
  }
  log.Fatal(s.ListenAndServe())
}

Route Specific Middleware

If you have a route group of routes that need specific middleware to be executed, you can simply create a new Negroni instance and use it as your route handler.

router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// add admin routes here

// Create a new negroni for the admin middleware
router.PathPrefix("/admin").Handler(negroni.New(
  Middleware1,
  Middleware2,
  negroni.Wrap(adminRoutes),
))

If you are using Gorilla Mux, here is an example using a subrouter:

router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"

// "/subpath" is necessary to ensure the subRouter and main router linkup
router.PathPrefix("/subpath").Handler(negroni.New(
  Middleware1,
  Middleware2,
  negroni.Wrap(subRouter),
))

With() can be used to eliminate redundancy for middlewares shared across routes.

router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// add api routes here
webRoutes := mux.NewRouter()
// add web routes here

// create common middleware to be shared across routes
common := negroni.New(
	Middleware1,
	Middleware2,
)

// create a new negroni for the api middleware
// using the common middleware as a base
router.PathPrefix("/api").Handler(common.With(
  APIMiddleware1,
  negroni.Wrap(apiRoutes),
))
// create a new negroni for the web middleware
// using the common middleware as a base
router.PathPrefix("/web").Handler(common.With(
  WebMiddleware1,
  negroni.Wrap(webRoutes),
))

Bundled Middleware

Static

This middleware will serve files on the filesystem. If the files do not exist, it proxies the request to the next middleware. If you want the requests for non-existent files to return a 404 File Not Found to the user you should look at using http.FileServer as a handler.

Example:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  // Example of using a http.FileServer if you want "server-like" rather than "middleware" behavior
  // mux.Handle("/public", http.FileServer(http.Dir("/home/public")))

  n := negroni.New()
  n.Use(negroni.NewStatic(http.Dir("/tmp")))
  n.UseHandler(mux)

  http.ListenAndServe(":3002", n)
}

Will serve files from the /tmp directory first, but proxy calls to the next handler if the request does not match a file on the filesystem.

Recovery

This middleware catches panics and responds with a 500 response code. If any other middleware has written a response code or body, this middleware will fail to properly send a 500 to the client, as the client has already received the HTTP response code. Additionally, an PanicHandlerFunc can be attached to report 500's to an error reporting service such as Sentry or Airbrake.

Example:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  n.Use(negroni.NewRecovery())
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

Will return a 500 Internal Server Error to each request. It will also log the stack traces as well as print the stack trace to the requester if PrintStack is set to true (the default).

Example with error handler:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  recovery := negroni.NewRecovery()
  recovery.PanicHandlerFunc = reportToSentry
  n.Use(recovery)
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

func reportToSentry(info *negroni.PanicInformation) {
    // write code here to report error to Sentry
}

The middleware simply output the informations on STDOUT by default. You can customize the output process by using the SetFormatter() function.

You can use also the HTMLPanicFormatter to display a pretty HTML when a crash occurs.

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  recovery := negroni.NewRecovery()
  recovery.Formatter = &negroni.HTMLPanicFormatter{}
  n.Use(recovery)
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

Logger

This middleware logs each incoming request and response.

Example:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.New()
  n.Use(negroni.NewLogger())
  n.UseHandler(mux)

  http.ListenAndServe(":3004", n)
}

Will print a log similar to:

[negroni] 2017-10-04T14:56:25+02:00 | 200 |      378µs | localhost:3004 | GET /

on each request.

You can also set your own log format by calling the SetFormat function. The format is a template string with fields as mentioned in the LoggerEntry struct. So, as an example -

l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")

will show something like - [200 18.263µs] - Go-User-Agent/1.1

Third Party Middleware

Here is a current list of Negroni compatible middlware. Feel free to put up a PR linking your middleware if you have built one:

Middleware Author Description
authz Yang Luo ACL, RBAC, ABAC Authorization middlware based on Casbin
binding Matt Holt Data binding from HTTP requests into structs
cloudwatch Colin Steele AWS cloudwatch metrics middleware
cors Olivier Poitrey Cross Origin Resource Sharing (CORS) support
csp Awake Networks Content Security Policy (CSP) support
delay Jeff Martinez Add delays/latency to endpoints. Useful when testing effects of high latency
New Relic Go Agent Yadvendar Champawat Official New Relic Go Agent (currently in beta)
gorelic Jingwen Owen Ou New Relic agent for Go runtime
Graceful Tyler Bunnell Graceful HTTP Shutdown
gzip phyber GZIP response compression
JWT Middleware Auth0 Middleware checks for a JWT on the Authorization header on incoming requests and decodes it
JWT Middleware Marcelo Fuentes JWT middleware for golang
logrus Dan Buch Logrus-based logger
oauth2 David Bochenski oAuth2 middleware
onthefly Alexander Rødseth Generate TinySVG, HTML and CSS on the fly
permissions2 Alexander Rødseth Cookies, users and permissions
prometheus Rene Zbinden Easily create metrics endpoint for the prometheus instrumentation tool
render Cory Jacobsen Render JSON, XML and HTML templates
RestGate Prasanga Siripala Secure authentication for REST API endpoints
secure Cory Jacobsen Middleware that implements a few quick security wins
sessions David Bochenski Session Management
stats Florent Messa Store information about your web application (response time, etc.)
VanGoH Taylor Wrobel Configurable AWS-Style HMAC authentication middleware
xrequestid Andrea Franz Middleware that assigns a random X-Request-Id header to each request
mgo session Joel James Middleware that handles creating and closing mgo sessions per request
digits Bilal Amarni Middleware that handles Twitter Digits authentication
stats Chirag Gupta Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db
Chaos Marc Falzon Middleware for injecting chaotic behavior into application in a programmatic way

Examples

Alexander Rødseth created mooseware, a skeleton for writing a Negroni middleware handler.

Prasanga Siripala created an effective skeleton structure for web-based Go/Negroni projects: Go-Skeleton

Live code reload?

gin and fresh both live reload negroni apps.

Essential Reading for Beginners of Go & Negroni

About

Negroni is obsessively designed by none other than the Code Gangsta

Documentation

Overview

Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.

If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.

For a full guide visit http://github.com/urfave/negroni

package main

import (
  "github.com/urfave/negroni"
  "net/http"
  "fmt"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic()
  n.UseHandler(mux)
  n.Run(":3000")
}

Index

Constants

View Source
const (
	// DefaultAddress is used if no other is specified.
	DefaultAddress = ":8080"
)

Variables

View Source
var LoggerDefaultDateFormat = time.RFC3339

LoggerDefaultDateFormat is the format used for date by the default Logger instance.

View Source
var LoggerDefaultFormat = "{{.StartTime}} | {{.Status}} | \t {{.Duration}} | {{.Hostname}} | {{.Method}} {{.Path}}"

LoggerDefaultFormat is the format logged used by the default Logger instance.

Functions

This section is empty.

Types

type ALogger added in v0.3.0

type ALogger interface {
	Println(v ...interface{})
	Printf(format string, v ...interface{})
}

ALogger interface

type HTMLPanicFormatter added in v0.3.0

type HTMLPanicFormatter struct{}

HTMLPanicFormatter output the stack inside an HTML page. This has been largely inspired by https://github.com/go-martini/martini/pull/156/commits.

func (*HTMLPanicFormatter) FormatPanicError added in v0.3.0

func (t *HTMLPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation)

type Handler

type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

Handler handler is an interface that objects can implement to be registered to serve as middleware in the Negroni middleware stack. ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc passed in.

If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.

func Wrap

func Wrap(handler http.Handler) Handler

Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni middleware. The next http.HandlerFunc is automatically called after the Handler is executed.

func WrapFunc added in v0.3.0

func WrapFunc(handlerFunc http.HandlerFunc) Handler

WrapFunc converts a http.HandlerFunc into a negroni.Handler so it can be used as a Negroni middleware. The next http.HandlerFunc is automatically called after the Handler is executed.

type HandlerFunc

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.

func (HandlerFunc) ServeHTTP

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

type Logger

type Logger struct {
	// ALogger implements just enough log.Logger interface to be compatible with other implementations
	ALogger
	// contains filtered or unexported fields
}

Logger is a middleware handler that logs the request as it goes in and the response as it goes out.

func NewLogger

func NewLogger() *Logger

NewLogger returns a new Logger instance

func (*Logger) ServeHTTP

func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (*Logger) SetDateFormat added in v0.3.0

func (l *Logger) SetDateFormat(format string)

func (*Logger) SetFormat added in v0.3.0

func (l *Logger) SetFormat(format string)

type LoggerEntry added in v0.3.0

type LoggerEntry struct {
	StartTime string
	Status    int
	Duration  time.Duration
	Hostname  string
	Method    string
	Path      string
	Request   *http.Request
}

LoggerEntry is the structure passed to the template.

type Negroni

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

Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler. Negroni middleware is evaluated in the order that they are added to the stack using the Use and UseHandler methods.

func Classic

func Classic() *Negroni

Classic returns a new Negroni instance with the default middleware already in the stack.

Recovery - Panic Recovery Middleware Logger - Request/Response Logging Static - Static File Serving

func New

func New(handlers ...Handler) *Negroni

New returns a new Negroni instance with no middleware preconfigured.

func (*Negroni) Handlers added in v0.2.0

func (n *Negroni) Handlers() []Handler

Returns a list of all the handlers in the current Negroni middleware chain.

func (*Negroni) Run

func (n *Negroni) Run(addr ...string)

Run is a convenience function that runs the negroni stack as an HTTP server. The addr string, if provided, takes the same format as http.ListenAndServe. If no address is provided but the PORT environment variable is set, the PORT value is used. If neither is provided, the address' value will equal the DefaultAddress constant.

func (*Negroni) ServeHTTP

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request)

func (*Negroni) Use

func (n *Negroni) Use(handler Handler)

Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.

func (*Negroni) UseFunc added in v0.2.0

func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc))

UseFunc adds a Negroni-style handler function onto the middleware stack.

func (*Negroni) UseHandler

func (n *Negroni) UseHandler(handler http.Handler)

UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.

func (*Negroni) UseHandlerFunc added in v0.2.0

func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request))

UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack.

func (*Negroni) With added in v0.3.0

func (n *Negroni) With(handlers ...Handler) *Negroni

With returns a new Negroni instance that is a combination of the negroni receiver's handlers and the provided handlers.

type PanicFormatter added in v0.3.0

type PanicFormatter interface {
	// FormatPanicError output the stack for a given answer/response.
	// In case the the middleware should not output the stack trace,
	// the field `Stack` of the passed `PanicInformation` instance equals `[]byte{}`.
	FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation)
}

PanicFormatter is an interface on object can implement to be able to output the stack trace

type PanicInformation added in v0.3.0

type PanicInformation struct {
	RecoveredPanic interface{}
	Stack          []byte
	Request        *http.Request
}

PanicInformation contains all elements for printing stack informations.

func (*PanicInformation) RequestDescription added in v0.3.0

func (p *PanicInformation) RequestDescription() string

RequestDescription returns a printable description of the url

func (*PanicInformation) StackAsString added in v0.3.0

func (p *PanicInformation) StackAsString() string

StackAsString returns a printable version of the stack

type Recovery

type Recovery struct {
	Logger           ALogger
	PrintStack       bool
	PanicHandlerFunc func(*PanicInformation)
	StackAll         bool
	StackSize        int
	Formatter        PanicFormatter

	// Deprecated: Use PanicHandlerFunc instead to receive panic
	// error with additional information (see PanicInformation)
	ErrorHandlerFunc func(interface{})
}

Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.

func NewRecovery

func NewRecovery() *Recovery

NewRecovery returns a new instance of Recovery

func (*Recovery) ServeHTTP

func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	http.Flusher
	// Status returns the status code of the response or 0 if the response has
	// not been written
	Status() int
	// Written returns whether or not the ResponseWriter has been written.
	Written() bool
	// Size returns the size of the response body.
	Size() int
	// Before allows for a function to be called before the ResponseWriter has been written to. This is
	// useful for setting headers or any other operations that must happen before a response has been written.
	Before(func(ResponseWriter))
}

ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about the response. It is recommended that middleware handlers use this construct to wrap a responsewriter if the functionality calls for it.

func NewResponseWriter

func NewResponseWriter(rw http.ResponseWriter) ResponseWriter

NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter

type Static

type Static struct {
	// Dir is the directory to serve static files from
	Dir http.FileSystem
	// Prefix is the optional prefix used to serve the static directory content
	Prefix string
	// IndexFile defines which file to serve as index if it exists.
	IndexFile string
}

Static is a middleware handler that serves static files in the given directory/filesystem. If the file does not exist on the filesystem, it passes along to the next middleware in the chain. If you desire "fileserver" type behavior where it returns a 404 for unfound files, you should consider using http.FileServer from the Go stdlib.

func NewStatic

func NewStatic(directory http.FileSystem) *Static

NewStatic returns a new instance of Static

func (*Static) ServeHTTP

func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

type TextPanicFormatter added in v0.3.0

type TextPanicFormatter struct{}

TextPanicFormatter output the stack as simple text on os.Stdout. If no `Content-Type` is set, it will output the data as `text/plain; charset=utf-8`. Otherwise, the origin `Content-Type` is kept.

func (*TextPanicFormatter) FormatPanicError added in v0.3.0

func (t *TextPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation)

Jump to

Keyboard shortcuts

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