add negroni dep
This commit is contained in:
parent
cd53c93c93
commit
d74bb2de15
|
@ -13,6 +13,12 @@
|
||||||
"revision": "1128ce320b775e8e3fa2b8095b9c2116aa6869db",
|
"revision": "1128ce320b775e8e3fa2b8095b9c2116aa6869db",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"importpath": "github.com/codegangsta/negroni",
|
||||||
|
"repository": "https://github.com/codegangsta/negroni",
|
||||||
|
"revision": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b",
|
||||||
|
"branch": "master"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"importpath": "github.com/layeh/gopher-json",
|
"importpath": "github.com/layeh/gopher-json",
|
||||||
"repository": "https://github.com/layeh/gopher-json",
|
"repository": "https://github.com/layeh/gopher-json",
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Jeremy Saenz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Negroni [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61)
|
||||||
|
|
||||||
|
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](http://github.com/go-martini/martini), but you think it contains too much magic, then Negroni is a great fit.
|
||||||
|
|
||||||
|
|
||||||
|
Language Translations:
|
||||||
|
* [Português Brasileiro (pt_BR)](translations/README_pt_br.md)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`.
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/codegangsta/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")
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Then install the Negroni package (**go 1.1** and greater is required):
|
||||||
|
~~~
|
||||||
|
go get github.com/codegangsta/negroni
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Then run your server:
|
||||||
|
~~~
|
||||||
|
go run server.go
|
||||||
|
~~~
|
||||||
|
|
||||||
|
You will now have a Go net/http webserver running on `localhost:3000`.
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
If you have a question or feature request, [go ask the mailing list](https://groups.google.com/forum/#!forum/negroni-users). The GitHub issues for Negroni will be used exclusively for bug reports and pull requests.
|
||||||
|
|
||||||
|
## Is Negroni a Framework?
|
||||||
|
Negroni is **not** a framework. It is a 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, Negroni tries to play well with all of them by fully supporting `net/http`. For instance, integrating with [Gorilla Mux](http://github.com/gorilla/mux) looks like so:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
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)
|
||||||
|
|
||||||
|
n.Run(":3000")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## `negroni.Classic()`
|
||||||
|
`negroni.Classic()` provides some default middleware that is useful for most applications:
|
||||||
|
|
||||||
|
* `negroni.Recovery` - Panic Recovery Middleware.
|
||||||
|
* `negroni.Logging` - Request/Response Logging Middleware.
|
||||||
|
* `negroni.Static` - Static File serving under the "public" directory.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
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:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
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:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.New()
|
||||||
|
n.Use(negroni.HandlerFunc(MyMiddleware))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
You can also map plain old `http.Handler`s:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.New()
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
// map your routes
|
||||||
|
|
||||||
|
n.UseHandler(mux)
|
||||||
|
|
||||||
|
n.Run(":3000")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## `Run()`
|
||||||
|
Negroni has a convenience function called `Run`. `Run` takes an addr string identical to [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe).
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.Classic()
|
||||||
|
// ...
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", n))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
router := mux.NewRouter()
|
||||||
|
adminRoutes := mux.NewRouter()
|
||||||
|
// add admin routes here
|
||||||
|
|
||||||
|
// Create a new negroni for the admin middleware
|
||||||
|
router.Handle("/admin", negroni.New(
|
||||||
|
Middleware1,
|
||||||
|
Middleware2,
|
||||||
|
negroni.Wrap(adminRoutes),
|
||||||
|
))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
| -----------|--------|-------------|
|
||||||
|
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
|
||||||
|
| [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
|
||||||
|
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
|
||||||
|
| [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it|
|
||||||
|
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
|
||||||
|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
|
||||||
|
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
|
||||||
|
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
|
||||||
|
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
|
||||||
|
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
|
||||||
|
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
|
||||||
|
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
|
||||||
|
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
|
||||||
|
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
|
||||||
|
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
|
||||||
|
| [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |
|
||||||
|
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
[Alexander Rødseth](https://github.com/xyproto) created [mooseware](https://github.com/xyproto/mooseware), a skeleton for writing a Negroni middleware handler.
|
||||||
|
|
||||||
|
## Live code reload?
|
||||||
|
[gin](https://github.com/codegangsta/gin) and [fresh](https://github.com/pilu/fresh) both live reload negroni apps.
|
||||||
|
|
||||||
|
## Essential Reading for Beginners of Go & Negroni
|
||||||
|
|
||||||
|
* [Using a Context to pass information from middleware to end handler](http://elithrar.github.io/article/map-string-interface/)
|
||||||
|
* [Understanding middleware](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters)
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Negroni is obsessively designed by none other than the [Code Gangsta](http://codegangsta.io/)
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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/codegangsta/negroni
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "github.com/codegangsta/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")
|
||||||
|
// }
|
||||||
|
package negroni
|
|
@ -0,0 +1,29 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
|
||||||
|
type Logger struct {
|
||||||
|
// Logger inherits from log.Logger used to log messages with the Logger middleware
|
||||||
|
*log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogger returns a new Logger instance
|
||||||
|
func NewLogger() *Logger {
|
||||||
|
return &Logger{log.New(os.Stdout, "[negroni] ", 0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
start := time.Now()
|
||||||
|
l.Printf("Started %s %s", r.Method, r.URL.Path)
|
||||||
|
|
||||||
|
next(rw, r)
|
||||||
|
|
||||||
|
res := rw.(ResponseWriter)
|
||||||
|
l.Printf("Completed %v %s in %v", res.Status(), http.StatusText(res.Status()), time.Since(start))
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Logger(t *testing.T) {
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
l := NewLogger()
|
||||||
|
l.Logger = log.New(buff, "[negroni] ", 0)
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
// replace log for testing
|
||||||
|
n.Use(l)
|
||||||
|
n.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
rw.WriteHeader(http.StatusNotFound)
|
||||||
|
}))
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:3000/foobar", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(recorder, req)
|
||||||
|
expect(t, recorder.Code, http.StatusNotFound)
|
||||||
|
refute(t, len(buff.String()), 0)
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
type Handler interface {
|
||||||
|
ServeHTTP(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.
|
||||||
|
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||||
|
|
||||||
|
func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
h(rw, r, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
type middleware struct {
|
||||||
|
handler Handler
|
||||||
|
next *middleware
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Wrap(handler http.Handler) Handler {
|
||||||
|
return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
handler.ServeHTTP(rw, r)
|
||||||
|
next(rw, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
type Negroni struct {
|
||||||
|
middleware middleware
|
||||||
|
handlers []Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Negroni instance with no middleware preconfigured.
|
||||||
|
func New(handlers ...Handler) *Negroni {
|
||||||
|
return &Negroni{
|
||||||
|
handlers: handlers,
|
||||||
|
middleware: build(handlers),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Classic() *Negroni {
|
||||||
|
return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
n.middleware.ServeHTTP(NewResponseWriter(rw), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
|
||||||
|
func (n *Negroni) Use(handler Handler) {
|
||||||
|
n.handlers = append(n.handlers, handler)
|
||||||
|
n.middleware = build(n.handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseFunc adds a Negroni-style handler function onto the middleware stack.
|
||||||
|
func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
|
||||||
|
n.Use(HandlerFunc(handlerFunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
|
||||||
|
func (n *Negroni) UseHandler(handler http.Handler) {
|
||||||
|
n.Use(Wrap(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseHandler adds a http.HandlerFunc-style handler function onto the middleware stack.
|
||||||
|
func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
|
||||||
|
n.UseHandler(http.HandlerFunc(handlerFunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run is a convenience function that runs the negroni stack as an HTTP
|
||||||
|
// server. The addr string takes the same format as http.ListenAndServe.
|
||||||
|
func (n *Negroni) Run(addr string) {
|
||||||
|
l := log.New(os.Stdout, "[negroni] ", 0)
|
||||||
|
l.Printf("listening on %s", addr)
|
||||||
|
l.Fatal(http.ListenAndServe(addr, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a list of all the handlers in the current Negroni middleware chain.
|
||||||
|
func (n *Negroni) Handlers() []Handler {
|
||||||
|
return n.handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
func build(handlers []Handler) middleware {
|
||||||
|
var next middleware
|
||||||
|
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
return voidMiddleware()
|
||||||
|
} else if len(handlers) > 1 {
|
||||||
|
next = build(handlers[1:])
|
||||||
|
} else {
|
||||||
|
next = voidMiddleware()
|
||||||
|
}
|
||||||
|
|
||||||
|
return middleware{handlers[0], &next}
|
||||||
|
}
|
||||||
|
|
||||||
|
func voidMiddleware() middleware {
|
||||||
|
return middleware{
|
||||||
|
HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
|
||||||
|
&middleware{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Test Helpers */
|
||||||
|
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||||
|
if a != b {
|
||||||
|
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||||
|
if a == b {
|
||||||
|
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegroniRun(t *testing.T) {
|
||||||
|
// just test that Run doesn't bomb
|
||||||
|
go New().Run(":3000")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNegroniServeHTTP(t *testing.T) {
|
||||||
|
result := ""
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
result += "foo"
|
||||||
|
next(rw, r)
|
||||||
|
result += "ban"
|
||||||
|
}))
|
||||||
|
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
result += "bar"
|
||||||
|
next(rw, r)
|
||||||
|
result += "baz"
|
||||||
|
}))
|
||||||
|
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
result += "bat"
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
}))
|
||||||
|
|
||||||
|
n.ServeHTTP(response, (*http.Request)(nil))
|
||||||
|
|
||||||
|
expect(t, result, "foobarbatbazban")
|
||||||
|
expect(t, response.Code, http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures that a Negroni middleware chain
|
||||||
|
// can correctly return all of its handlers.
|
||||||
|
func TestHandlers(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
n := New()
|
||||||
|
handlers := n.Handlers()
|
||||||
|
expect(t, 0, len(handlers))
|
||||||
|
|
||||||
|
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Expects the length of handlers to be exactly 1
|
||||||
|
// after adding exactly one handler to the middleware chain
|
||||||
|
handlers = n.Handlers()
|
||||||
|
expect(t, 1, len(handlers))
|
||||||
|
|
||||||
|
// Ensures that the first handler that is in sequence behaves
|
||||||
|
// exactly the same as the one that was registered earlier
|
||||||
|
handlers[0].ServeHTTP(response, (*http.Request)(nil), nil)
|
||||||
|
expect(t, response.Code, http.StatusOK)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
|
||||||
|
type Recovery struct {
|
||||||
|
Logger *log.Logger
|
||||||
|
PrintStack bool
|
||||||
|
StackAll bool
|
||||||
|
StackSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecovery returns a new instance of Recovery
|
||||||
|
func NewRecovery() *Recovery {
|
||||||
|
return &Recovery{
|
||||||
|
Logger: log.New(os.Stdout, "[negroni] ", 0),
|
||||||
|
PrintStack: true,
|
||||||
|
StackAll: false,
|
||||||
|
StackSize: 1024 * 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
stack := make([]byte, rec.StackSize)
|
||||||
|
stack = stack[:runtime.Stack(stack, rec.StackAll)]
|
||||||
|
|
||||||
|
f := "PANIC: %s\n%s"
|
||||||
|
rec.Logger.Printf(f, err, stack)
|
||||||
|
|
||||||
|
if rec.PrintStack {
|
||||||
|
fmt.Fprintf(rw, f, err, stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
next(rw, r)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRecovery(t *testing.T) {
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
rec := NewRecovery()
|
||||||
|
rec.Logger = log.New(buff, "[negroni] ", 0)
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
// replace log for testing
|
||||||
|
n.Use(rec)
|
||||||
|
n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
panic("here is a panic!")
|
||||||
|
}))
|
||||||
|
n.ServeHTTP(recorder, (*http.Request)(nil))
|
||||||
|
expect(t, recorder.Code, http.StatusInternalServerError)
|
||||||
|
refute(t, recorder.Body.Len(), 0)
|
||||||
|
refute(t, len(buff.String()), 0)
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
type beforeFunc func(ResponseWriter)
|
||||||
|
|
||||||
|
// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
|
||||||
|
func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
|
||||||
|
return &responseWriter{rw, 0, 0, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
status int
|
||||||
|
size int
|
||||||
|
beforeFuncs []beforeFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) WriteHeader(s int) {
|
||||||
|
rw.status = s
|
||||||
|
rw.callBefore()
|
||||||
|
rw.ResponseWriter.WriteHeader(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Write(b []byte) (int, error) {
|
||||||
|
if !rw.Written() {
|
||||||
|
// The status will be StatusOK if WriteHeader has not been called yet
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
size, err := rw.ResponseWriter.Write(b)
|
||||||
|
rw.size += size
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Status() int {
|
||||||
|
return rw.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Size() int {
|
||||||
|
return rw.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Written() bool {
|
||||||
|
return rw.status != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Before(before func(ResponseWriter)) {
|
||||||
|
rw.beforeFuncs = append(rw.beforeFuncs, before)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
hijacker, ok := rw.ResponseWriter.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
||||||
|
}
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) CloseNotify() <-chan bool {
|
||||||
|
return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) callBefore() {
|
||||||
|
for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
|
||||||
|
rw.beforeFuncs[i](rw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) Flush() {
|
||||||
|
flusher, ok := rw.ResponseWriter.(http.Flusher)
|
||||||
|
if ok {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type closeNotifyingRecorder struct {
|
||||||
|
*httptest.ResponseRecorder
|
||||||
|
closed chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
|
||||||
|
return &closeNotifyingRecorder{
|
||||||
|
httptest.NewRecorder(),
|
||||||
|
make(chan bool, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *closeNotifyingRecorder) close() {
|
||||||
|
c.closed <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
|
||||||
|
return c.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
type hijackableResponse struct {
|
||||||
|
Hijacked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHijackableResponse() *hijackableResponse {
|
||||||
|
return &hijackableResponse{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hijackableResponse) Header() http.Header { return nil }
|
||||||
|
func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0, nil }
|
||||||
|
func (h *hijackableResponse) WriteHeader(code int) {}
|
||||||
|
func (h *hijackableResponse) Flush() {}
|
||||||
|
func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
h.Hijacked = true
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterWritingString(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
|
||||||
|
rw.Write([]byte("Hello world"))
|
||||||
|
|
||||||
|
expect(t, rec.Code, rw.Status())
|
||||||
|
expect(t, rec.Body.String(), "Hello world")
|
||||||
|
expect(t, rw.Status(), http.StatusOK)
|
||||||
|
expect(t, rw.Size(), 11)
|
||||||
|
expect(t, rw.Written(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterWritingStrings(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
|
||||||
|
rw.Write([]byte("Hello world"))
|
||||||
|
rw.Write([]byte("foo bar bat baz"))
|
||||||
|
|
||||||
|
expect(t, rec.Code, rw.Status())
|
||||||
|
expect(t, rec.Body.String(), "Hello worldfoo bar bat baz")
|
||||||
|
expect(t, rw.Status(), http.StatusOK)
|
||||||
|
expect(t, rw.Size(), 26)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterWritingHeader(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
|
||||||
|
rw.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
|
expect(t, rec.Code, rw.Status())
|
||||||
|
expect(t, rec.Body.String(), "")
|
||||||
|
expect(t, rw.Status(), http.StatusNotFound)
|
||||||
|
expect(t, rw.Size(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterBefore(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
result := ""
|
||||||
|
|
||||||
|
rw.Before(func(ResponseWriter) {
|
||||||
|
result += "foo"
|
||||||
|
})
|
||||||
|
rw.Before(func(ResponseWriter) {
|
||||||
|
result += "bar"
|
||||||
|
})
|
||||||
|
|
||||||
|
rw.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
|
expect(t, rec.Code, rw.Status())
|
||||||
|
expect(t, rec.Body.String(), "")
|
||||||
|
expect(t, rw.Status(), http.StatusNotFound)
|
||||||
|
expect(t, rw.Size(), 0)
|
||||||
|
expect(t, result, "barfoo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterHijack(t *testing.T) {
|
||||||
|
hijackable := newHijackableResponse()
|
||||||
|
rw := NewResponseWriter(hijackable)
|
||||||
|
hijacker, ok := rw.(http.Hijacker)
|
||||||
|
expect(t, ok, true)
|
||||||
|
_, _, err := hijacker.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
expect(t, hijackable.Hijacked, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriteHijackNotOK(t *testing.T) {
|
||||||
|
hijackable := new(http.ResponseWriter)
|
||||||
|
rw := NewResponseWriter(*hijackable)
|
||||||
|
hijacker, ok := rw.(http.Hijacker)
|
||||||
|
expect(t, ok, true)
|
||||||
|
_, _, err := hijacker.Hijack()
|
||||||
|
|
||||||
|
refute(t, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterCloseNotify(t *testing.T) {
|
||||||
|
rec := newCloseNotifyingRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
closed := false
|
||||||
|
notifier := rw.(http.CloseNotifier).CloseNotify()
|
||||||
|
rec.close()
|
||||||
|
select {
|
||||||
|
case <-notifier:
|
||||||
|
closed = true
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
}
|
||||||
|
expect(t, closed, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterFlusher(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
rw := NewResponseWriter(rec)
|
||||||
|
|
||||||
|
_, ok := rw.(http.Flusher)
|
||||||
|
expect(t, ok, true)
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Static is a middleware handler that serves static files in the given directory/filesystem.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatic returns a new instance of Static
|
||||||
|
func NewStatic(directory http.FileSystem) *Static {
|
||||||
|
return &Static{
|
||||||
|
Dir: directory,
|
||||||
|
Prefix: "",
|
||||||
|
IndexFile: "index.html",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
if r.Method != "GET" && r.Method != "HEAD" {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := r.URL.Path
|
||||||
|
// if we have a prefix, filter requests by stripping the prefix
|
||||||
|
if s.Prefix != "" {
|
||||||
|
if !strings.HasPrefix(file, s.Prefix) {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file = file[len(s.Prefix):]
|
||||||
|
if file != "" && file[0] != '/' {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f, err := s.Dir.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
// discard the error?
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to serve index file
|
||||||
|
if fi.IsDir() {
|
||||||
|
// redirect if missing trailing slash
|
||||||
|
if !strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file = path.Join(file, s.IndexFile)
|
||||||
|
f, err = s.Dir.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err = f.Stat()
|
||||||
|
if err != nil || fi.IsDir() {
|
||||||
|
next(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(rw, r, file, fi.ModTime(), f)
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package negroni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStatic(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
response.Body = new(bytes.Buffer)
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
n.Use(NewStatic(http.Dir(".")))
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
expect(t, response.Code, http.StatusOK)
|
||||||
|
expect(t, response.Header().Get("Expires"), "")
|
||||||
|
if response.Body.Len() == 0 {
|
||||||
|
t.Errorf("Got empty body for GET request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticHead(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
response.Body = new(bytes.Buffer)
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
n.Use(NewStatic(http.Dir(".")))
|
||||||
|
n.UseHandler(http.NotFoundHandler())
|
||||||
|
|
||||||
|
req, err := http.NewRequest("HEAD", "http://localhost:3000/negroni.go", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
expect(t, response.Code, http.StatusOK)
|
||||||
|
if response.Body.Len() != 0 {
|
||||||
|
t.Errorf("Got non-empty body for HEAD request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticAsPost(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
n.Use(NewStatic(http.Dir(".")))
|
||||||
|
n.UseHandler(http.NotFoundHandler())
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:3000/negroni.go", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
expect(t, response.Code, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticBadDir(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
n := Classic()
|
||||||
|
n.UseHandler(http.NotFoundHandler())
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
refute(t, response.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticOptionsServeIndex(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
s := NewStatic(http.Dir("."))
|
||||||
|
s.IndexFile = "negroni.go"
|
||||||
|
n.Use(s)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:3000/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
expect(t, response.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticOptionsPrefix(t *testing.T) {
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
n := New()
|
||||||
|
s := NewStatic(http.Dir("."))
|
||||||
|
s.Prefix = "/public"
|
||||||
|
n.Use(s)
|
||||||
|
|
||||||
|
// Check file content behaviour
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:3000/public/negroni.go", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.ServeHTTP(response, req)
|
||||||
|
expect(t, response.Code, http.StatusOK)
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
# Negroni [![GoDoc](https://godoc.org/github.com/codegangsta/negroni?status.svg)](http://godoc.org/github.com/codegangsta/negroni) [![wercker status](https://app.wercker.com/status/13688a4a94b82d84a0b8d038c4965b61/s "wercker status")](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61)
|
||||||
|
|
||||||
|
Negroni é uma abordagem idiomática para middleware web em Go. É pequeno, não intrusivo, e incentiva uso da biblioteca `net/http`.
|
||||||
|
|
||||||
|
Se gosta da idéia do [Martini](http://github.com/go-martini/martini), mas acha que contém muita mágica, então Negroni é ideal.
|
||||||
|
|
||||||
|
## Começando
|
||||||
|
|
||||||
|
Depois de instalar Go e definir seu [GOPATH](http://golang.org/doc/code.html#GOPATH), criar seu primeirto arquivo `.go`. Iremos chamá-lo `server.go`.
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/codegangsta/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")
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Depois instale o pacote Negroni (**go 1.1** ou superior)
|
||||||
|
~~~
|
||||||
|
go get github.com/codegangsta/negroni
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Depois execute seu servidor:
|
||||||
|
~~~
|
||||||
|
go run server.go
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Agora terá um servidor web Go net/http rodando em `localhost:3000`.
|
||||||
|
|
||||||
|
## Precisa de Ajuda?
|
||||||
|
Se você tem uma pergunta ou pedido de recurso,[go ask the mailing list](https://groups.google.com/forum/#!forum/negroni-users). O Github issues para o Negroni será usado exclusivamente para Reportar bugs e pull requests.
|
||||||
|
|
||||||
|
## Negroni é um Framework?
|
||||||
|
Negroni **não** é a framework. É uma biblioteca que é desenhada para trabalhar diretamente com net/http.
|
||||||
|
|
||||||
|
## Roteamento?
|
||||||
|
Negroni é TSPR(Traga seu próprio Roteamento). A comunidade Go já tem um grande número de roteadores http disponíveis, Negroni tenta rodar bem com todos eles pelo suporte total `net/http`/ Por exemplo, a integração com [Gorilla Mux](http://github.com/gorilla/mux) se parece com isso:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
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)
|
||||||
|
|
||||||
|
n.Run(":3000")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## `negroni.Classic()`
|
||||||
|
`negroni.Classic()` fornece alguns middlewares padrão que são úteis para maioria das aplicações:
|
||||||
|
|
||||||
|
* `negroni.Recovery` - Panic Recovery Middleware.
|
||||||
|
* `negroni.Logging` - Request/Response Logging Middleware.
|
||||||
|
* `negroni.Static` - Static File serving under the "public" directory.
|
||||||
|
|
||||||
|
Isso torna muito fácil começar com alguns recursos úteis do Negroni.
|
||||||
|
|
||||||
|
## Handlers
|
||||||
|
Negroni fornece um middleware de fluxo bidirecional. Isso é feito através da interface `negroni.Handler`:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
type Handler interface {
|
||||||
|
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Se um middleware não tenha escrito o ResponseWriter, ele deve chamar a próxima `http.HandlerFunc` na cadeia para produzir o próximo handler middleware. Isso pode ser usado muito bem:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
// do some stuff before
|
||||||
|
next(rw, r)
|
||||||
|
// do some stuff after
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
E pode mapear isso para a cadeia de handler com a função `Use`:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.New()
|
||||||
|
n.Use(negroni.HandlerFunc(MyMiddleware))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Você também pode mapear `http.Handler` antigos:
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.New()
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
// map your routes
|
||||||
|
|
||||||
|
n.UseHandler(mux)
|
||||||
|
|
||||||
|
n.Run(":3000")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## `Run()`
|
||||||
|
Negroni tem uma função de conveniência chamada `Run`. `Run` pega um endereço de string idêntico para [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe).
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
n := negroni.Classic()
|
||||||
|
// ...
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", n))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Middleware para Rotas Específicas
|
||||||
|
Se você tem um grupo de rota com rotas que precisam ser executadas por um middleware específico, pode simplesmente criar uma nova instância de Negroni e usar no seu Manipulador de rota.
|
||||||
|
|
||||||
|
~~~ go
|
||||||
|
router := mux.NewRouter()
|
||||||
|
adminRoutes := mux.NewRouter()
|
||||||
|
// add admin routes here
|
||||||
|
|
||||||
|
// Criar um middleware negroni para admin
|
||||||
|
router.Handle("/admin", negroni.New(
|
||||||
|
Middleware1,
|
||||||
|
Middleware2,
|
||||||
|
negroni.Wrap(adminRoutes),
|
||||||
|
))
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Middleware de Terceiros
|
||||||
|
|
||||||
|
Aqui está uma lista atual de Middleware Compatíveis com Negroni. Sinta se livre para mandar um PR vinculando seu middleware se construiu um:
|
||||||
|
|
||||||
|
|
||||||
|
| Middleware | Autor | Descrição |
|
||||||
|
| -----------|--------|-------------|
|
||||||
|
| [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
|
||||||
|
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Implementa rapidamente itens de segurança.|
|
||||||
|
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Handler para mapeamento/validação de um request a estrutura. |
|
||||||
|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
|
||||||
|
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Pacote para renderizar JSON, XML, e templates HTML. |
|
||||||
|
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
|
||||||
|
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | Handler para adicionar compreção gzip para as requisições |
|
||||||
|
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | Handler que prove sistema de login OAuth 2.0 para aplicações Martini. Google Sign-in, Facebook Connect e Github login são suportados. |
|
||||||
|
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Handler que provê o serviço de sessão. |
|
||||||
|
| [permissions](https://github.com/xyproto/permissions) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, usuários e permissões. |
|
||||||
|
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Pacote para gerar TinySVG, HTML e CSS em tempo real. |
|
||||||
|
|
||||||
|
## Exemplos
|
||||||
|
[Alexander Rødseth](https://github.com/xyproto) criou [mooseware](https://github.com/xyproto/mooseware), uma estrutura para escrever um handler middleware Negroni.
|
||||||
|
|
||||||
|
## Servidor com autoreload?
|
||||||
|
[gin](https://github.com/codegangsta/gin) e [fresh](https://github.com/pilu/fresh) são aplicativos para autoreload do Negroni.
|
||||||
|
|
||||||
|
## Leitura Essencial para Iniciantes em Go & Negroni
|
||||||
|
* [Usando um contexto para passar informação de um middleware para o manipulador final](http://elithrar.github.io/article/map-string-interface/)
|
||||||
|
* [Entendendo middleware](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters)
|
||||||
|
|
||||||
|
|
||||||
|
## Sobre
|
||||||
|
Negroni é obsessivamente desenhado por ninguém menos que [Code Gangsta](http://codegangsta.io/)
|
Loading…
Reference in New Issue