Negroni
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()
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)
n.Use(Middleware3)
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) {
next(rw, r)
}
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.Handler
s:
n := negroni.New()
mux := http.NewServeMux()
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.
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)
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()
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()
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)
subRouter.HandleFunc("/:id", someSubpathHandler)
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()
webRoutes := mux.NewRouter()
common := negroni.New(
Middleware1,
Middleware2,
)
router.PathPrefix("/api").Handler(common.With(
APIMiddleware1,
negroni.Wrap(apiRoutes),
))
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!")
})
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 panic
s 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) {
}
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:
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