Repository: urfave/negroni
Branch: master
Commit: 1af389b71c45
Files: 28
Total size: 176.2 KB
Directory structure:
gitextract_j1egrmu8/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc.go
├── go.mod
├── logger.go
├── logger_test.go
├── negroni.go
├── negroni_bench_test.go
├── negroni_test.go
├── recovery.go
├── recovery_test.go
├── response_writer.go
├── response_writer_feature.go
├── response_writer_pusher.go
├── response_writer_pusher_test.go
├── response_writer_test.go
├── static.go
├── static_test.go
└── translations/
├── README_de_de.md
├── README_fr_FR.md
├── README_ja_JP.md
├── README_ko_KR.md
├── README_pt_br.md
├── README_zh_CN.md
└── README_zh_tw.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/coverage.txt
================================================
FILE: .travis.yml
================================================
language: go
sudo: false
dist: trusty
arch:
- AMD64
- ppc64le
go:
- 1.x
- 1.2.x
- 1.3.x
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- master
# Disable version 1.x, 1.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x, 1.7.x, 1.8.x, 1.9.x, 1.10.x
jobs:
exclude:
- arch: ppc64le
go: 1.x
- arch: ppc64le
go: 1.2.x
- arch: ppc64le
go: 1.3.x
- arch: ppc64le
go: 1.4.x
- arch: ppc64le
go: 1.5.x
- arch: ppc64le
go: 1.6.x
- arch: ppc64le
go: 1.7.x
- arch: ppc64le
go: 1.8.x
- arch: ppc64le
go: 1.9.x
- arch: ppc64le
go: 1.10.x
before_install:
- find "${GOPATH%%:*}" -name '*.a' -delete
- rm -rf "${GOPATH%%:*}/src/golang.org"
- go get golang.org/x/tools/cover
- go get golang.org/x/tools/cmd/cover
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s "https://codecov.io/bash")
================================================
FILE: CHANGELOG.md
================================================
# Change Log
**ATTN**: This project uses [semantic versioning](http://semver.org/).
## [3.1.1] - [2024-06-04]
### Fixed
- Writing an HTTP 1xx status codes no longer results in a 200 being sent in
addition given the expectation is that a follow-up status code will be written
later. The exception is `101 Switching Protocols` since this is terminal. This
matches `net/http` behavior.
## [3.1.0] - [2023-10-07]
### Fixed
- `WriteHeader` can again be used in the `ResponseWriter.Before` callback to
set HTTP status codes
### Added
- Satisfy `http.ResponseController` (Go 1.20+)
## [3.0.0] - [2022-09-18]
### Fixed
- Replace multiple slashes at the beginning of a path with a single one to avoid
open redirects
- Avoid updating `ResponseWriter.Status()` if the status has already been
written when `ResponseWriter.WriteHeader()` is called twice
### Changed
- `ResponseWriter` now only implements `http` interfaces (`Flusher`, `Hijacker`,
`CloseNotifier`) if the wrapped `http.ResponseWriter` does. This is a breaking
change to make `ResponseWriter`'s interface support more accurate
### Added
- `ResponseWriter` now implements `io.ReaderFrom` to more efficiently send
responses via `io.Copy`
## [2.0.2] - 2020-07-17
### Fixed
- Fixed Go module name for v2
## [2.0.1] - 2020-05-25
### Fixed
- Recovery middleware now checks that `Formatter` is not `nil` before calling
## [2.0.0] - 2020-05-25
### Changed
- `Recovery.PrintStack`, when false, now also supresses the panic message in
addition to supressing the stack trace
### Fixed
- `Negroni.With()` now copies handlers to avoid mutating the original `Negroni`
instance if `Use` is called on the new `Negroni` instance
### Added
- `Recovery.LogStack` was added to control whether the stacktrace is logged for
panics
### Changed
## [1.0.0] - 2018-09-01
### Fixed
- `Logger` middleware now correctly handles paths containing a `%` instead of trying to treat it as a format specifier
## [0.3.0] - 2017-11-11
### Added
- `With()` helper for building a new `Negroni` struct chaining handlers from
existing `Negroni` structs
- Format log output in `Logger` middleware via a configurable `text/template`
string injectable via `.SetFormat`. Added `LoggerDefaultFormat` and
`LoggerDefaultDateFormat` to configure the default template and date format
used by the `Logger` middleware.
- Support for HTTP/2 pusher support via `http.Pusher` interface for Go 1.8+.
- `WrapFunc` to convert `http.HandlerFunc` into a `negroni.Handler`
- `Formatter` field added to `Recovery` middleware to allow configuring how
`panic`s are output. Default of `TextFormatter` (how it was output in
`0.2.0`) used. `HTMLPanicFormatter` also added to allow easy outputing of
`panic`s as HTML.
### Fixed
- `Written()` correct returns `false` if no response header has been written
- Only implement `http.CloseNotifier` with the `negroni.ResponseWriter` if the
underlying `http.ResponseWriter` implements it (previously would always
implement it and panic if the underlying `http.ResponseWriter` did not.
### Changed
- Set default status to `0` in the case that no handler writes status -- was
previously `200` (in 0.2.0, before that it was `0` so this reestablishes that
behavior)
- Catch `panic`s thrown by callbacks provided to the `Recovery` handler
- Recovery middleware will set `text/plain` content-type if none is set
- `ALogger` interface to allow custom logger outputs to be used with the
`Logger` middleware. Changes embeded field in `negroni.Logger` from `Logger`
to `ALogger`.
- Default `Logger` middleware output changed to be more structure and verbose
(also now configurable, see `Added`)
- Automatically bind to port specified in `$PORT` in `.Run()` if an address is
not passed in. Fall back to binding to `:8080` if no address specified
(configuable via `DefaultAddress`).
- `PanicHandlerFunc` added to `Recovery` middleware to enhance custom handling
of `panic`s by providing additional information to the handler including the
stack and the `http.Request`. `Recovery.ErrorHandlerFunc` was also added, but
deprecated in favor of the new `PanicHandlerFunc`.
## [0.2.0] - 2016-05-10
### Added
- Support for variadic handlers in `New()`
- Added `Negroni.Handlers()` to fetch all of the handlers for a given chain
- Allowed size in `Recovery` handler was bumped to 8k
- `Negroni.UseFunc` to push another handler onto the chain
### Changed
- Set the status before calling `beforeFuncs` so the information is available to them
- Set default status to `200` in the case that no handler writes status -- was previously `0`
- Panic if `nil` handler is given to `negroni.Use`
## 0.1.0 - 2013-07-22
### Added
- Initial implementation.
[Unreleased]: https://github.com/urfave/negroni/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/urfave/negroni/compare/v0.1.0...v0.2.0
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2025 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.
================================================
FILE: README.md
================================================
# Negroni
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/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](https://github.com/go-martini/martini), but
you think it contains too much magic, then Negroni is a great fit.
Language Translations:
* [Deutsch (de_DE)](translations/README_de_de.md)
* [Português Brasileiro (pt_BR)](translations/README_pt_br.md)
* [简体中文 (zh_CN)](translations/README_zh_CN.md)
* [繁體中文 (zh_TW)](translations/README_zh_tw.md)
* [日本語 (ja_JP)](translations/README_ja_JP.md)
* [Français (fr_FR)](translations/README_fr_FR.md)
* [한국어 (ko_KR)](translations/README_ko_KR.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`.
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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/v3
```
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](https://packages.debian.org/sid/golang-github-urfave-negroni-dev) 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:
``` 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)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()`
`negroni.Classic()` provides some default middleware that is useful for most
applications:
* [`negroni.Recovery`](#recovery) - Panic Recovery Middleware.
* [`negroni.Logger`](#logger) - Request/Response Logger Middleware.
* [`negroni.Static`](#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)
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.
```go
// 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`](https://godoc.org/net/http#ListenAndServe).
<!-- { "interrupt": true } -->
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
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](https://godoc.org/github.com/urfave/negroni/v3#Negroni.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.:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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.
``` go
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:
``` go
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.
``` go
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](https://golang.org/pkg/net/http/#FileServer) as
a handler.
Example:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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 `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:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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:
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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.
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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 -
```go
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](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
| [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 |
| [JWT Middleware](https://github.com/mfuentesg/go-jwtmiddleware) | [Marcelo Fuentes](https://github.com/mfuentesg) | JWT middleware for golang |
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
| [prometheus](https://github.com/slok/go-prometheus-middleware) | [Xabier Larrakoetxea](https://github.com/slok) | [Prometheus](http://prometheus.io) metrics with multiple options that follow standards and try to be measured in a efficient way |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
| [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 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
| [Chaos](https://github.com/falzm/chaos) | [Marc Falzon](https://github.com/falzm) | Middleware for injecting chaotic behavior into application in a programmatic way |
## Examples
[Alexander Rødseth](https://github.com/xyproto) created
[mooseware](https://github.com/xyproto/mooseware), a skeleton for writing a
Negroni middleware handler.
[Prasanga Siripala](https://github.com/pjebs) created an effective skeleton structure for web-based Go/Negroni projects: [Go-Skeleton](https://github.com/pjebs/go-skeleton)
## 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](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
## About
Negroni is obsessively designed by none other than the [Code
Gangsta](https://codegangsta.io/)
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
================================================
FILE: doc.go
================================================
// 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")
// }
package negroni
================================================
FILE: go.mod
================================================
module github.com/urfave/negroni/v3
go 1.17
================================================
FILE: logger.go
================================================
package negroni
import (
"bytes"
"log"
"net/http"
"os"
"text/template"
"time"
)
// LoggerEntry is the structure passed to the template.
type LoggerEntry struct {
StartTime string
Status int
Duration time.Duration
Hostname string
Method string
Path string
Request *http.Request
}
// LoggerDefaultFormat is the format logged used by the default Logger instance.
var LoggerDefaultFormat = "{{.StartTime}} | {{.Status}} | \t {{.Duration}} | {{.Hostname}} | {{.Method}} {{.Path}}"
// LoggerDefaultDateFormat is the format used for date by the default Logger instance.
var LoggerDefaultDateFormat = time.RFC3339
// ALogger interface
type ALogger interface {
Println(v ...interface{})
Printf(format string, v ...interface{})
}
// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
type Logger struct {
// ALogger implements just enough log.Logger interface to be compatible with other implementations
ALogger
dateFormat string
template *template.Template
}
// NewLogger returns a new Logger instance
func NewLogger() *Logger {
logger := &Logger{ALogger: log.New(os.Stdout, "[negroni] ", 0), dateFormat: LoggerDefaultDateFormat}
logger.SetFormat(LoggerDefaultFormat)
return logger
}
func (l *Logger) SetFormat(format string) {
l.template = template.Must(template.New("negroni_parser").Parse(format))
}
func (l *Logger) SetDateFormat(format string) {
l.dateFormat = format
}
func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
start := time.Now()
next(rw, r)
res := rw.(ResponseWriter)
log := LoggerEntry{
StartTime: start.Format(l.dateFormat),
Status: res.Status(),
Duration: time.Since(start),
Hostname: r.Host,
Method: r.Method,
Path: r.URL.Path,
Request: r,
}
buff := &bytes.Buffer{}
l.template.Execute(buff, log)
l.Println(buff.String())
}
================================================
FILE: logger_test.go
================================================
package negroni
import (
"bytes"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func Test_Logger(t *testing.T) {
var buff bytes.Buffer
recorder := httptest.NewRecorder()
l := NewLogger()
l.ALogger = 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)
}
func Test_LoggerURLEncodedString(t *testing.T) {
var buff bytes.Buffer
recorder := httptest.NewRecorder()
l := NewLogger()
l.ALogger = log.New(&buff, "[negroni] ", 0)
l.SetFormat("{{.Path}}")
n := New()
// replace log for testing
n.Use(l)
n.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
// Test reserved characters - !*'();:@&=+$,/?%#[]
req, err := http.NewRequest("GET", "http://localhost:3000/%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D", nil)
if err != nil {
t.Error(err)
}
n.ServeHTTP(recorder, req)
expect(t, recorder.Code, http.StatusOK)
expect(t, strings.TrimSpace(buff.String()), "[negroni] /!*'();:@&=+$,/?%#[]")
refute(t, len(buff.String()), 0)
}
func Test_LoggerCustomFormat(t *testing.T) {
var buff bytes.Buffer
recorder := httptest.NewRecorder()
l := NewLogger()
l.ALogger = log.New(&buff, "[negroni] ", 0)
l.SetFormat("{{.Request.URL.Query.Get \"foo\"}} {{.Request.UserAgent}} - {{.Status}}")
n := New()
n.Use(l)
n.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("OK"))
}))
userAgent := "Negroni-Test"
req, err := http.NewRequest("GET", "http://localhost:3000/foobar?foo=bar", nil)
if err != nil {
t.Error(err)
}
req.Header.Set("User-Agent", userAgent)
n.ServeHTTP(recorder, req)
expect(t, strings.TrimSpace(buff.String()), "[negroni] bar "+userAgent+" - 200")
}
================================================
FILE: negroni.go
================================================
package negroni
import (
"log"
"net/http"
"os"
)
const (
// DefaultAddress is used if no other is specified.
DefaultAddress = ":8080"
)
func init() {
initFeaturePicker()
}
// 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
// nextfn stores the next.ServeHTTP to reduce memory allocate
nextfn func(rw http.ResponseWriter, r *http.Request)
}
func newMiddleware(handler Handler, next *middleware) middleware {
return middleware{
handler: handler,
nextfn: next.ServeHTTP,
}
}
func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
m.handler.ServeHTTP(rw, r, m.nextfn)
}
// 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)
})
}
// 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.
func WrapFunc(handlerFunc http.HandlerFunc) Handler {
return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
handlerFunc(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),
}
}
// With returns a new Negroni instance that is a combination of the negroni
// receiver's handlers and the provided handlers.
func (n *Negroni) With(handlers ...Handler) *Negroni {
currentHandlers := make([]Handler, len(n.handlers))
copy(currentHandlers, n.handlers)
return New(
append(currentHandlers, 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) {
if handler == nil {
panic("handler cannot be nil")
}
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))
}
// UseHandlerFunc 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, 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 (n *Negroni) Run(addr ...string) {
l := log.New(os.Stdout, "[negroni] ", 0)
finalAddr := detectAddress(addr...)
l.Printf("listening on %s", finalAddr)
l.Fatal(http.ListenAndServe(finalAddr, n))
}
func detectAddress(addr ...string) string {
if len(addr) > 0 {
return addr[0]
}
if port := os.Getenv("PORT"); port != "" {
return ":" + port
}
return DefaultAddress
}
// 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
switch {
case len(handlers) == 0:
return voidMiddleware()
case len(handlers) > 1:
next = build(handlers[1:])
default:
next = voidMiddleware()
}
return newMiddleware(handlers[0], &next)
}
func voidMiddleware() middleware {
return newMiddleware(
HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
&middleware{},
)
}
================================================
FILE: negroni_bench_test.go
================================================
package negroni
import (
"net/http"
"testing"
)
type voidHandler struct{}
func (vh *voidHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
next(rw, r)
}
func BenchmarkNegroni(b *testing.B) {
h1 := &voidHandler{}
h2 := &voidHandler{}
h3 := &voidHandler{}
h4 := &voidHandler{}
h5 := &voidHandler{}
h6 := &voidHandler{}
h7 := &voidHandler{}
h8 := &voidHandler{}
h9 := &voidHandler{}
h10 := &voidHandler{}
n := New(h1, h2, h3, h4, h5, h6, h7, h8, h9, h10)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
n.ServeHTTP(nil, nil)
}
}
================================================
FILE: negroni_test.go
================================================
package negroni
import (
"net/http"
"net/http/httptest"
"os"
"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 TestNegroniWith(t *testing.T) {
result := ""
response := httptest.NewRecorder()
n1 := New()
n1.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result = "one"
next(rw, r)
}))
n1.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result += "two"
next(rw, r)
}))
n1.ServeHTTP(response, (*http.Request)(nil))
expect(t, 2, len(n1.Handlers()))
expect(t, result, "onetwo")
n2 := n1.With(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result += "three"
next(rw, r)
}))
// Verify that n1 was left intact and not modified.
n1.ServeHTTP(response, (*http.Request)(nil))
expect(t, 2, len(n1.Handlers()))
expect(t, result, "onetwo")
n2.ServeHTTP(response, (*http.Request)(nil))
expect(t, 3, len(n2.Handlers()))
expect(t, result, "onetwothree")
}
func TestNegroniWith_doNotModifyOriginal(t *testing.T) {
result := ""
response := httptest.NewRecorder()
n1 := New()
n1.handlers = make([]Handler, 0, 10) // enforce initial capacity
n1.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result = "one"
next(rw, r)
}))
n1.ServeHTTP(response, (*http.Request)(nil))
expect(t, 1, len(n1.Handlers()))
n2 := n1.With(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result += "two"
next(rw, r)
}))
n3 := n1.With(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
result += "three"
next(rw, r)
}))
// rebuilds middleware
n2.UseHandlerFunc(func(rw http.ResponseWriter, r *http.Request) {})
n3.UseHandlerFunc(func(rw http.ResponseWriter, r *http.Request) {})
n1.ServeHTTP(response, (*http.Request)(nil))
expect(t, 1, len(n1.Handlers()))
expect(t, result, "one")
n2.ServeHTTP(response, (*http.Request)(nil))
expect(t, 3, len(n2.Handlers()))
expect(t, result, "onetwo")
n3.ServeHTTP(response, (*http.Request)(nil))
expect(t, 3, len(n3.Handlers()))
expect(t, result, "onethree")
}
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)
}
func TestNegroni_Use_Nil(t *testing.T) {
defer func() {
err := recover()
if err == nil {
t.Errorf("Expected negroni.Use(nil) to panic, but it did not")
}
}()
n := New()
n.Use(nil)
}
func TestDetectAddress(t *testing.T) {
if detectAddress() != DefaultAddress {
t.Error("Expected the DefaultAddress")
}
if detectAddress(":6060") != ":6060" {
t.Error("Expected the provided address")
}
os.Setenv("PORT", "8080")
if detectAddress() != ":8080" {
t.Error("Expected the PORT env var with a prefixed colon")
}
}
func voidHTTPHandlerFunc(rw http.ResponseWriter, r *http.Request) {
// Do nothing
}
// Test for function Wrap
func TestWrap(t *testing.T) {
response := httptest.NewRecorder()
handler := Wrap(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
handler.ServeHTTP(response, (*http.Request)(nil), voidHTTPHandlerFunc)
expect(t, response.Code, http.StatusOK)
}
// Test for function WrapFunc
func TestWrapFunc(t *testing.T) {
response := httptest.NewRecorder()
// WrapFunc(f) equals Wrap(http.HandlerFunc(f)), it's simpler and useful.
handler := WrapFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
})
handler.ServeHTTP(response, (*http.Request)(nil), voidHTTPHandlerFunc)
expect(t, response.Code, http.StatusOK)
}
================================================
FILE: recovery.go
================================================
package negroni
import (
"fmt"
"log"
"net/http"
"os"
"runtime"
"runtime/debug"
"text/template"
)
const (
// NoPrintStackBodyString is the body content returned when HTTP stack printing is suppressed
NoPrintStackBodyString = "500 Internal Server Error"
panicText = "PANIC: %s\n%s"
panicHTML = `<html>
<head><title>PANIC: {{.RecoveredPanic}}</title></head>
<style type="text/css">
html, body {
font-family: Helvetica, Arial, Sans;
color: #333333;
background-color: #ffffff;
margin: 0px;
}
h1 {
color: #ffffff;
background-color: #f14c4c;
padding: 20px;
border-bottom: 1px solid #2b3848;
}
.block {
margin: 2em;
}
.panic-interface {
}
.panic-stack-raw pre {
padding: 1em;
background: #f6f8fa;
border: dashed 1px;
}
.panic-interface-title {
font-weight: bold;
}
</style>
<body>
<h1>Negroni - PANIC</h1>
<div class="panic-interface block">
<h3>{{.RequestDescription}}</h3>
<span class="panic-interface-title">Runtime error:</span> <span class="panic-interface-element">{{.RecoveredPanic}}</span>
</div>
{{ if .Stack }}
<div class="panic-stack-raw block">
<h3>Runtime Stack</h3>
<pre>{{.StackAsString}}</pre>
</div>
{{ end }}
</body>
</html>`
nilRequestMessage = "Request is nil"
)
var panicHTMLTemplate = template.Must(template.New("PanicPage").Parse(panicHTML))
// PanicInformation contains all
// elements for printing stack informations.
type PanicInformation struct {
RecoveredPanic interface{}
Stack []byte
Request *http.Request
}
// StackAsString returns a printable version of the stack
func (p *PanicInformation) StackAsString() string {
return string(p.Stack)
}
// RequestDescription returns a printable description of the url
func (p *PanicInformation) RequestDescription() string {
if p.Request == nil {
return nilRequestMessage
}
var queryOutput string
if p.Request.URL.RawQuery != "" {
queryOutput = "?" + p.Request.URL.RawQuery
}
return fmt.Sprintf("%s %s%s", p.Request.Method, p.Request.URL.Path, queryOutput)
}
// PanicFormatter is an interface on object can implement
// to be able to output the stack trace
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)
}
// 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.
type TextPanicFormatter struct{}
func (t *TextPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
if rw.Header().Get("Content-Type") == "" {
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
}
fmt.Fprintf(rw, panicText, infos.RecoveredPanic, infos.Stack)
}
// HTMLPanicFormatter output the stack inside
// an HTML page. This has been largely inspired by
// https://github.com/go-martini/martini/pull/156/commits.
type HTMLPanicFormatter struct{}
func (t *HTMLPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
if rw.Header().Get("Content-Type") == "" {
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
}
panicHTMLTemplate.Execute(rw, infos)
}
// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
type Recovery struct {
Logger ALogger
PrintStack bool
LogStack 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{})
}
// NewRecovery returns a new instance of Recovery
func NewRecovery() *Recovery {
return &Recovery{
Logger: log.New(os.Stdout, "[negroni] ", 0),
PrintStack: true,
LogStack: true,
StackAll: false,
StackSize: 1024 * 8,
Formatter: &TextPanicFormatter{},
}
}
func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
defer func() {
if err := recover(); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
infos := &PanicInformation{
RecoveredPanic: err,
Request: r,
Stack: make([]byte, rec.StackSize),
}
infos.Stack = infos.Stack[:runtime.Stack(infos.Stack, rec.StackAll)]
// PrintStack will write stack trace info to the ResponseWriter if set to true!
// If set to false it will respond with the standard response documented here https://httpstat.us/500
if rec.PrintStack && rec.Formatter != nil {
rec.Formatter.FormatPanicError(rw, r, infos)
} else {
if rw.Header().Get("Content-Type") == "" {
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
}
fmt.Fprint(rw, NoPrintStackBodyString)
}
if rec.LogStack {
rec.Logger.Printf(panicText, err, infos.Stack)
}
if rec.ErrorHandlerFunc != nil {
func() {
defer func() {
if err := recover(); err != nil {
rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
rec.Logger.Printf("%s\n", debug.Stack())
}
}()
rec.ErrorHandlerFunc(err)
}()
}
if rec.PanicHandlerFunc != nil {
func() {
defer func() {
if err := recover(); err != nil {
rec.Logger.Printf("provided PanicHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
rec.Logger.Printf("%s\n", debug.Stack())
}
}()
rec.PanicHandlerFunc(infos)
}()
}
}
}()
next(rw, r)
}
================================================
FILE: recovery_test.go
================================================
package negroni
import (
"bytes"
"fmt"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestRecovery(t *testing.T) {
buff := bytes.NewBufferString("")
recorder := httptest.NewRecorder()
panicHandlerCalled := false
handlerCalled := false
rec := NewRecovery()
rec.Logger = log.New(buff, "[negroni] ", 0)
rec.ErrorHandlerFunc = func(i interface{}) {
handlerCalled = true
}
rec.PanicHandlerFunc = func(i *PanicInformation) {
panicHandlerCalled = (i != nil)
}
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.Header().Get("Content-Type"), "text/plain; charset=utf-8")
expect(t, recorder.Code, http.StatusInternalServerError)
expect(t, panicHandlerCalled, true)
expect(t, handlerCalled, true)
refute(t, recorder.Body.Len(), 0)
refute(t, len(buff.String()), 0)
}
func TestRecovery_noContentTypeOverwrite(t *testing.T) {
recorder := httptest.NewRecorder()
rec := NewRecovery()
rec.Logger = log.New(bytes.NewBuffer([]byte{}), "[negroni] ", 0)
n := New()
n.Use(rec)
n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "application/javascript; charset=utf-8")
panic("here is a panic!")
}))
n.ServeHTTP(recorder, (*http.Request)(nil))
expect(t, recorder.Header().Get("Content-Type"), "application/javascript; charset=utf-8")
}
func TestRecovery_callbackPanic(t *testing.T) {
buff := bytes.NewBufferString("")
recorder := httptest.NewRecorder()
rec := NewRecovery()
rec.Logger = log.New(buff, "[negroni] ", 0)
rec.ErrorHandlerFunc = func(i interface{}) {
panic("callback panic")
}
n := New()
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, strings.Contains(buff.String(), "callback panic"), true)
}
func TestRecovery_handlerPanic(t *testing.T) {
buff := bytes.NewBufferString("")
recorder := httptest.NewRecorder()
rec := NewRecovery()
rec.Logger = log.New(buff, "[negroni] ", 0)
rec.PanicHandlerFunc = func(i *PanicInformation) {
panic("panic handler panic")
}
n := New()
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, strings.Contains(buff.String(), "panic handler panic"), true)
}
type testOutput struct {
*bytes.Buffer
}
func newTestOutput() *testOutput {
buf := bytes.NewBufferString("")
return &testOutput{buf}
}
func (t *testOutput) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
fmt.Fprintf(t, formatInfos(infos))
}
func formatInfos(infos *PanicInformation) string {
return fmt.Sprintf("%s %s", infos.RequestDescription(), infos.RecoveredPanic)
}
func TestRecovery_formatter(t *testing.T) {
recorder := httptest.NewRecorder()
formatter := newTestOutput()
req, _ := http.NewRequest("GET", "http://localhost:3003/somePath?element=true", nil)
var element interface{} = "here is a panic!"
expectedInfos := &PanicInformation{RecoveredPanic: element, Request: req}
rec := NewRecovery()
rec.Formatter = formatter
n := New()
n.Use(rec)
n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
panic(element)
}))
n.ServeHTTP(recorder, req)
expect(t, formatInfos(expectedInfos), formatter.String())
}
func TestRecovery_PanicInformation(t *testing.T) {
// Request with query
req, _ := http.NewRequest("GET", "http://localhost:3003/somePath?element=true", nil)
var element interface{} = "here is a panic!"
expectedInfos := &PanicInformation{RecoveredPanic: element, Request: req}
expect(t, expectedInfos.RequestDescription(), "GET /somePath?element=true")
// Request without Query
req, _ = http.NewRequest("POST", "http://localhost:3003/somePath", nil)
element = "here is a panic!"
expectedInfos = &PanicInformation{RecoveredPanic: element, Request: req}
expect(t, expectedInfos.RequestDescription(), "POST /somePath")
// Nil request
expectedInfos = &PanicInformation{RecoveredPanic: element, Request: nil}
expect(t, expectedInfos.RequestDescription(), nilRequestMessage)
// Stack
stackValue := "Some Stack element"
expectedInfos = &PanicInformation{RecoveredPanic: element, Request: req, Stack: []byte(stackValue)}
expect(t, expectedInfos.StackAsString(), stackValue)
}
func TestRecovery_HTMLFormatter(t *testing.T) {
recorder := httptest.NewRecorder()
rec := NewRecovery()
rec.Formatter = &HTMLPanicFormatter{}
n := New()
n.Use(rec)
n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
panic("some panic")
}))
n.ServeHTTP(recorder, (*http.Request)(nil))
expect(t, recorder.Header().Get("Content-Type"), "text/html; charset=utf-8")
refute(t, recorder.Body.Len(), 0)
}
================================================
FILE: response_writer.go
================================================
package negroni
import (
"io"
"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
// 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 a http.ResponseWriter
func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
nrw := &responseWriter{
ResponseWriter: rw,
}
return wrapFeature(nrw)
}
type responseWriter struct {
http.ResponseWriter
pendingStatus int
status int
size int
beforeFuncs []beforeFunc
callingBefores bool
}
func (rw *responseWriter) WriteHeader(s int) {
if rw.Written() {
return
}
rw.pendingStatus = s
rw.callBefore()
// Any of the rw.beforeFuncs may have written a header,
// so check again to see if any work is necessary.
if rw.Written() {
return
}
rw.status = s
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
}
// ReadFrom exposes underlying http.ResponseWriter to io.Copy and if it implements
// io.ReaderFrom, it can take advantage of optimizations such as sendfile, io.Copy
// with sync.Pool's buffer which is in http.(*response).ReadFrom and so on.
func (rw *responseWriter) ReadFrom(r io.Reader) (n int64, err error) {
if !rw.Written() {
// The status will be StatusOK if WriteHeader has not been called yet
rw.WriteHeader(http.StatusOK)
}
n, err = io.Copy(rw.ResponseWriter, r)
rw.size += int(n)
return
}
// Satisfy http.ResponseController support (Go 1.20+)
func (rw *responseWriter) Unwrap() http.ResponseWriter {
return rw.ResponseWriter
}
func (rw *responseWriter) Status() int {
if rw.Written() {
return rw.status
}
return rw.pendingStatus
}
func (rw *responseWriter) Size() int {
return rw.size
}
func (rw *responseWriter) Written() bool {
return rw.status >= http.StatusOK || rw.status == http.StatusSwitchingProtocols // treat all 1xx codes aside from SwitchingProtocols as non-terminal
}
func (rw *responseWriter) Before(before func(ResponseWriter)) {
rw.beforeFuncs = append(rw.beforeFuncs, before)
}
func (rw *responseWriter) callBefore() {
// Don't recursively call before() functions, to avoid infinite looping if
// one of them calls rw.WriteHeader again.
if rw.callingBefores {
return
}
rw.callingBefores = true
defer func() { rw.callingBefores = false }()
for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
rw.beforeFuncs[i](rw)
}
}
================================================
FILE: response_writer_feature.go
================================================
package negroni
import (
"bufio"
"net"
"net/http"
)
const (
flusher = 1 << iota
hijacker
closeNotifier
)
type (
flusherFeature struct{ *responseWriter }
hijackerFeature struct{ *responseWriter }
closeNotifierFeature struct{ *responseWriter }
)
func (f flusherFeature) Flush() {
if !f.Written() {
// The status will be StatusOK if WriteHeader has not been called yet
f.WriteHeader(http.StatusOK)
}
f.ResponseWriter.(http.Flusher).Flush()
}
func (f hijackerFeature) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return f.ResponseWriter.(http.Hijacker).Hijack()
}
func (f closeNotifierFeature) CloseNotify() <-chan bool {
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
var featurePicker = make([]func(writer *responseWriter) ResponseWriter, 8)
func initFeaturePicker() {
featurePicker[0] = func(w *responseWriter) ResponseWriter {
return w
}
featurePicker[flusher] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Flusher
}{w, flusherFeature{w}}
}
featurePicker[hijacker] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Hijacker
}{w, hijackerFeature{w}}
}
featurePicker[closeNotifier] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Flusher
}{w, flusherFeature{w}}
}
featurePicker[flusher|hijacker] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Flusher
http.Hijacker
}{w, flusherFeature{w}, hijackerFeature{w}}
}
featurePicker[flusher|closeNotifier] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Flusher
http.CloseNotifier
}{w, flusherFeature{w}, closeNotifierFeature{w}}
}
featurePicker[hijacker|closeNotifier] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Hijacker
http.CloseNotifier
}{w, hijackerFeature{w}, closeNotifierFeature{w}}
}
featurePicker[flusher|hijacker|closeNotifier] = func(w *responseWriter) ResponseWriter {
return struct {
*responseWriter
http.Flusher
http.Hijacker
http.CloseNotifier
}{w, flusherFeature{w}, hijackerFeature{w}, closeNotifierFeature{w}}
}
}
func wrapFeature(w *responseWriter) ResponseWriter {
rw := w.ResponseWriter
feature := 0
if _, ok := rw.(http.Flusher); ok {
feature |= flusher
}
if _, ok := rw.(http.Hijacker); ok {
feature |= hijacker
}
if _, ok := rw.(http.CloseNotifier); ok {
feature |= closeNotifier
}
return featurePicker[feature](w)
}
================================================
FILE: response_writer_pusher.go
================================================
//+build go1.8
package negroni
import (
"errors"
"net/http"
)
func (rw *responseWriter) Push(target string, opts *http.PushOptions) error {
pusher, ok := rw.ResponseWriter.(http.Pusher)
if ok {
return pusher.Push(target, opts)
}
return errors.New("the ResponseWriter doesn't support the Pusher interface")
}
================================================
FILE: response_writer_pusher_test.go
================================================
//+build go1.8
package negroni
import (
"net/http"
"net/http/httptest"
"testing"
)
type pusherRecorder struct {
*httptest.ResponseRecorder
pushed bool
}
func newPusherRecorder() *pusherRecorder {
return &pusherRecorder{ResponseRecorder: httptest.NewRecorder()}
}
func (c *pusherRecorder) Push(target string, opts *http.PushOptions) error {
c.pushed = true
return nil
}
func TestResponseWriterPush(t *testing.T) {
pushable := newPusherRecorder()
rw := NewResponseWriter(pushable)
pusher, ok := rw.(http.Pusher)
expect(t, ok, true)
err := pusher.Push("", nil)
if err != nil {
t.Error(err)
}
expect(t, pushable.pushed, true)
}
================================================
FILE: response_writer_test.go
================================================
package negroni
import (
"bufio"
"io"
"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 TestResponseWriterBeforeWrite(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
expect(t, rw.Status(), 0)
expect(t, rw.Written(), false)
}
func TestResponseWriterBeforeFuncHasAccessToStatus(t *testing.T) {
var status int
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
rw.Before(func(w ResponseWriter) {
status = w.Status()
})
rw.WriteHeader(http.StatusCreated)
expect(t, status, http.StatusCreated)
}
func TestResponseWriterBeforeFuncCanChangeStatus(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
// Always respond with 200.
rw.Before(func(w ResponseWriter) {
w.WriteHeader(http.StatusOK)
})
rw.WriteHeader(http.StatusBadRequest)
expect(t, rec.Code, http.StatusOK)
}
func TestResponseWriterBeforeFuncChangesStatusMultipleTimes(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
rw.Before(func(w ResponseWriter) {
w.WriteHeader(http.StatusInternalServerError)
})
rw.Before(func(w ResponseWriter) {
w.WriteHeader(http.StatusNotFound)
})
rw.WriteHeader(http.StatusOK)
expect(t, rec.Code, http.StatusNotFound)
}
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 TestResponseWriterWrittenStatusCode(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
expect(t, rw.Written(), false)
for status := http.StatusContinue; status <= http.StatusEarlyHints; status++ {
if status == http.StatusSwitchingProtocols {
continue
}
rw.WriteHeader(status)
expected := false
expect(t, rw.Written(), expected)
}
rw.WriteHeader(http.StatusCreated)
expect(t, rw.Written(), true)
rw2 := NewResponseWriter(rec)
expect(t, rw2.Written(), false)
rw2.WriteHeader(http.StatusSwitchingProtocols)
expect(t, rw2.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 TestResponseWriterWritingHeaderTwice(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
rw.WriteHeader(http.StatusNotFound)
rw.WriteHeader(http.StatusInternalServerError)
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 TestResponseWriterUnwrap(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
switch v := rw.(type) {
case interface{ Unwrap() http.ResponseWriter }:
expect(t, v.Unwrap(), rec)
default:
t.Error("Does not implement Unwrap()")
}
}
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)
_, ok := rw.(http.Hijacker)
expect(t, ok, false)
}
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 TestResponseWriterNonCloseNotify(t *testing.T) {
rw := NewResponseWriter(httptest.NewRecorder())
_, ok := rw.(http.CloseNotifier)
expect(t, ok, false)
}
func TestResponseWriterFlusher(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
_, ok := rw.(http.Flusher)
expect(t, ok, true)
}
func TestResponseWriter_Flush_marksWritten(t *testing.T) {
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
rw.(http.Flusher).Flush()
expect(t, rw.Status(), http.StatusOK)
expect(t, rw.Written(), true)
}
// mockReader only implements io.Reader without other methods like WriterTo
type mockReader struct {
readStr string
eof bool
}
func (r *mockReader) Read(p []byte) (n int, err error) {
if r.eof {
return 0, io.EOF
}
copy(p, []byte(r.readStr))
r.eof = true
return len(r.readStr), nil
}
func TestResponseWriterWithoutReadFrom(t *testing.T) {
writeString := "Hello world"
rec := httptest.NewRecorder()
rw := NewResponseWriter(rec)
n, err := io.Copy(rw, &mockReader{readStr: writeString})
expect(t, err, nil)
expect(t, rw.Status(), http.StatusOK)
expect(t, rw.Written(), true)
expect(t, rw.Size(), len(writeString))
expect(t, int(n), len(writeString))
expect(t, rec.Body.String(), writeString)
}
type mockResponseWriterWithReadFrom struct {
*httptest.ResponseRecorder
writtenStr string
}
func (rw *mockResponseWriterWithReadFrom) ReadFrom(r io.Reader) (n int64, err error) {
bytes, err := io.ReadAll(r)
if err != nil {
return 0, err
}
rw.writtenStr = string(bytes)
rw.ResponseRecorder.Write(bytes)
return int64(len(bytes)), nil
}
func TestResponseWriterWithReadFrom(t *testing.T) {
writeString := "Hello world"
mrw := &mockResponseWriterWithReadFrom{ResponseRecorder: httptest.NewRecorder()}
rw := NewResponseWriter(mrw)
n, err := io.Copy(rw, &mockReader{readStr: writeString})
expect(t, err, nil)
expect(t, rw.Status(), http.StatusOK)
expect(t, rw.Written(), true)
expect(t, rw.Size(), len(writeString))
expect(t, int(n), len(writeString))
expect(t, mrw.Body.String(), writeString)
expect(t, mrw.writtenStr, writeString)
}
================================================
FILE: static.go
================================================
package negroni
import (
"net/http"
"path"
"strings"
)
// 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.
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, "/") {
if strings.HasPrefix(r.URL.Path, "//") {
r.URL.Path = "/" + strings.TrimLeft(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)
}
================================================
FILE: static_test.go
================================================
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)
}
================================================
FILE: translations/README_de_de.md
================================================
# Negroni [](http://godoc.org/github.com/urfave/negroni/v3) [](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61)
Negroni ist ein Ansatz für eine idiomatische Middleware in Go. Sie ist klein, nicht-intrusiv und unterstützt die Nutzung von `net/http` Handlern.
Wenn Dir die Idee hinter [Martini](http://github.com/go-martini/martini) gefällt, aber Du denkst, es stecke zu viel Magie darin, dann ist Negroni eine passende Alternative.
## Wo fange ich an?
Nachdem Du Go installiert und den [GOPATH](http://golang.org/doc/code.html#GOPATH) eingerichtet hast, erstelle eine `.go`-Datei. Nennen wir sie `server.go`.
~~~ go
package main
import (
"github.com/urfave/negroni/v3"
"net/http"
"fmt"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Willkommen auf der Homepage!")
})
n := negroni.Classic()
n.UseHandler(mux)
n.Run(":3000")
}
~~~
Installiere nun das Negroni Package (**go 1.1** und höher werden vorausgesetzt):
~~~
go get github.com/urfave/negroni/v3
~~~
Dann starte Deinen Server:
~~~
go run server.go
~~~
Nun läuft ein `net/http`-Webserver von Go unter `localhost:3000`.
## Hilfe benötigt?
Wenn Du eine Frage hast oder Dir ein bestimmte Funktion wünscht, nutze die [Mailing Liste](https://groups.google.com/forum/#!forum/negroni-users). Issues auf Github werden ausschließlich für Bug Reports und Pull Requests genutzt.
## Ist Negroni ein Framework?
Negroni ist **kein** Framework. Es ist eine Bibliothek, geschaffen, um kompatibel mit `net/http` zu sein.
## Routing?
Negroni ist BYOR (Bring your own Router - Nutze Deinen eigenen Router). Die Go-Community verfügt bereits über eine Vielzahl von großartigen Routern. Negroni versucht möglichst alle zu unterstützen, indem es `net/http` vollständig unterstützt. Beispielsweise sieht eine Implementation mit [Gorilla Mux](http://github.com/gorilla/mux) folgendermaßen aus:
~~~ go
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
n := negroni.New(Middleware1, Middleware2)
// Oder nutze eine Middleware mit der Use()-Funktion
n.Use(Middleware3)
// Der Router kommt als letztes
n.UseHandler(router)
n.Run(":3000")
~~~
## `negroni.Classic()`
`negroni.Classic()` stellt einige Standard-Middlewares bereit, die für die meisten Anwendungen von Nutzen ist:
* `negroni.Recovery` - Middleware für Panic Recovery .
* `negroni.Logging` - Anfrage/Rückmeldungs-Logging-Middleware.
* `negroni.Static` - Ausliefern von statischen Dateien unter dem "public" Verzeichnis.
Dies macht es wirklich einfach, mit den nützlichen Funktionen von Negroni zu starten.
## Handlers
Negroni stellt einen bidirektionalen Middleware-Flow bereit. Dies wird durch das `negroni.Handler`-Interface erreicht:
~~~ go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
~~~
Wenn eine Middleware nicht bereits den ResponseWriter genutzt hat, sollte sie die nächste `http.HandlerFunc` in der Verkettung von Middlewares aufrufen und diese ausführen. Das kann von großem Nutzen sein:
~~~ go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// Mache etwas vor dem Aufruf
next(rw, r)
// Mache etwas nach dem Aufruf
}
~~~
Und Du kannst eine Middleware durch die `Use`-Funktion der Verkettung von Middlewares zuordnen.
~~~ go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
~~~
Stattdessen kannst Du auch herkömmliche `http.Handler` zuordnen:
~~~ go
n := negroni.New()
mux := http.NewServeMux()
// Ordne Deine Routen zu
n.UseHandler(mux)
n.Run(":3000")
~~~
## `Run()`
Negroni hat eine nützliche Funktion namens `Run`. `Run` übernimmt eine Zeichenkette `addr` ähnlich wie [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe).
~~~ go
n := negroni.Classic()
// ...
log.Fatal(http.ListenAndServe(":8080", n))
~~~
## Routenspezifische Middleware
Wenn Du eine Gruppe von Routen hast, welche alle die gleiche Middleware ausführen müssen, kannst Du einfach eine neue Negroni-Instanz erstellen und sie als Route-Handler nutzen:
~~~ go
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// Füge die Admin-Routen hier hinzu
// Erstelle eine neue Negroni-Instanz für die Admin-Middleware
router.Handle("/admin", negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
~~~
## Middlewares von Dritten
Hier ist eine aktuelle Liste von Middlewares, die kompatible mit Negroni sind. Tue Dir keinen Zwang an, Dich einzutragen, wenn Du selbst eine Middleware programmiert hast:
| Middleware | Autor | Beschreibung |
| -----------|--------|-------------|
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
| [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|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
| [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 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
## Beispiele
[Alexander Rødseth](https://github.com/xyproto) programmierte [mooseware](https://github.com/xyproto/mooseware), ein Grundgerüst zum Erstellen von Negroni Middleware-Handerln.
## Aktualisieren in Echtzeit?
[gin](https://github.com/codegangsta/gin) und [fresh](https://github.com/pilu/fresh) aktualisieren Deine Negroni-Anwendung automatisch.
## Unverzichbare Informationen für Go- & Negronineulinge
* [Nutze einen Kontext zum Übertragen von Middlewareinformationen an Handler (Englisch)](http://elithrar.github.io/article/map-string-interface/)
* [Middlewares verstehen (Englisch)](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters)
## Über das Projekt
Negroni wurde obsseziv von Niemand gerigeren als dem [Code Gangsta](http://codegangsta.io/) entwickelt.
================================================
FILE: translations/README_fr_FR.md
================================================
# Negroni
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/negroni)
**Note:** Ce projet était initiallement connu comme
`github.com/codegangsta/negroni` -- Github redirigera automatiquement les requêtes vers ce dépôt.
Nous vous recommandons néanmoins d'utiliser la référence vers ce nouveau dépôt pour plus de clarté.
Negroni approche la question de la création de *middleware* de manière pragmatique.
La librairie se veut légère, non intrusive et encourage l'utilisation des *Handlers* de
la librairie standard `net/http`.
Si vous appréciez le projet [Martini](https://github.com/go-martini/martini) et estimez
qu'une certaine magie s'en dégage, Negroni sera sans doute plus approprié.
## Démarrer avec Negroni
Une fois Go installé et votre variable [GOPATH](http://golang.org/doc/code.html#GOPATH) à jour,
créez votre premier fichier `.go` et nommez le `server.go`.
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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() // Inclue les "middlewares" par défaut.
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
```
Installez au préalable le paquet Negroni (**NOTE**: Une version de Go >= **go 1.1** est nécessaire):
```
go get github.com/urfave/negroni/v3
```
Démarrez le serveur:
```
go run server.go
```
Vous avez dès à présent un serveur web Go basé sur `net/http` disponible à l'adresse `localhost:3000`.
### Paquets
Si vous utilisez Debian, `negroni` est aussi disponible en tant que [paquet]
(https://packages.debian.org/sid/golang-github-urfave-negroni-dev).
La commande `apt install golang-github-urfave-negroni-dev` vous permettra de l'installer
(À ce jour, vous les trouverez dans les dépôts `sid`).
## Negroni est-il un *framework* ?
Negroni **n'est pas** un *framework*. Considérez le comme une librairie centrée sur
l'utilisation de *middleware* développés pour fonctionner directement avec la librairie `net/http`.
## Redirection (*Routing*) ?
Negroni est *BYOR* (*Bring your own Router*, Apporter votre propre routeur).
La communauté Go offre un nombre important de routeurs et Negroni met tout en oeuvre
pour fonctionner avec chacun d'entre eux en assurant un support complet de la librairie `net/http`.
Par exemple, une utilisation avec [Gorilla Mux] se présente sous la forme:
``` go
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
n := negroni.New(Middleware1, Middleware2)
// on peut utiliser également la fonction Use() pour ajouter un "middleware"
n.Use(Middleware3)
// le routeur se trouve toujours en dernier.
n.UseHandler(router)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()`
L'instance `negroni.Classic()` propose par défaut trois middlewares qui seront utiles à la plupart
des applications:
* [`negroni.Recovery`](#recovery) - Récupère des appels à `panic`.
* [`negroni.Logger`](#logger) - Journalise les requêtes et les réponses.
* [`negroni.Static`](#static) - Sers les fichiers statiques présents dans le dossier "public".
Elle offre un démarrage aisé sans recourir à la configuration pour utiliser quelques-unes des fonctions les plus utiles de Negroni.
## *Handlers*
Negroni offre un flux bidirectionnel via les *middlewares* (aller-retour entre la requête et la réponse). Tout repose sur l'interface `negroni.Handler` :
``` go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
Si un *middleware* n'a pas écrit au `ResponseWriter`, il doit faire appel au prochain `http.Handlerfunc` de la chaîne pour que le prochain *middleware* soit appelé:
``` go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// faire quelque chose avant
next(rw, r)
// faire quelque chose après
}
```
Vous pouvez insérer votre *middleware* dans la chaîne en utilisant la fonction `Use`:
``` go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
```
Vous pour également utiliser un `http.Handler` classique:
``` go
n := negroni.New()
mux := http.NewServeMux()
// définissez vos routes
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
```
## `With()`
La méthode `With()` vous permet de regrouper un ou plusieurs `Handler` au sein
d'une nouvelle instance `Negroni`. Cette dernière est la combinaison des `Handlers` de l'ancienne et de la nouvelle instance.
```go
// "middleware" à réutiliser.
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)
// `specific` devient une nouvelle instance avec les "handlers" provenant de `common` ainsi
// que ceux passés en paramètres.
specific := common.With(
SpecificMiddleware1,
SpecificMiddleware2
)
```
## `Run()`
Negroni peut-être démarré en utilisant la méthode `Run()`. Cette dernière
prend en paramètre l'adresse du serveur, à l'instar de la méthode [`http.ListenAndServe`](https://godoc.org/net/http#ListenAndServe).
<!-- { "interrupt": true } -->
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
Si aucune adresse n'est renseignée, la variable d'environnement `PORT` est utilisée.
Si cette dernière n'est pas définie, l'adresse par défaut est utilisée.
Pour une description détaillée, veuillez-vous référer à la documentation de la méthode
[Run]((https://godoc.org/github.com/urfave/negroni/v3#Negroni.Run).
De manière générale, vous voudrez vous servir de la librairie `net/http` et utiliser `negroni`
comme un simple `Handler` pour plus de flexibilité.
Par exemple:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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() // Inclue les "middlewares" par défaut
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())
}
```
## Redirection spécifique
Si un ensemble de routes nécessite l'appel à des *middleware* spécifiques,
vous pouvez simplement créer une nouvelle instance Negroni et l'utiliser comme
`Handler` pour cet ensemble.
``` go
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// ajout des routes relatives à l'administration
// Création d'une nouvelle instance pour le "middleware" admin.
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
```
Si vous utilisez [Gorilla Mux], vous pourriez utiliser un *subrouter*:
``` go
router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"
// "/subpath" est nécessaire pour assurer la cohésion entre le `subrouter` et le routeur principal
router.PathPrefix("/subpath").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(subRouter),
))
```
La méthode `With()` peut aider à réduire la duplication des *middlewares* partagés par
plusieurs routes.
``` go
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// ajout des routes api ici
webRoutes := mux.NewRouter()
// ajout des routes web ici
// création d'un "middleware" commun pour faciliter le partage
common := negroni.New(
Middleware1,
Middleware2,
)
// création d'une nouvelle instance pour le "middleware"
// api en utilisant le "middleware" commun comme base.
router.PathPrefix("/api").Handler(common.With(
APIMiddleware1,
negroni.Wrap(apiRoutes),
))
// création d'une nouvelle instance pour le "middleware"
// web en utilisant le "middleware" commun comme base.
router.PathPrefix("/web").Handler(common.With(
WebMiddleware1,
negroni.Wrap(webRoutes),
))
```
## *Middlewares* fournis
### Static
Ce *middleware* va servir les fichiers présents sur le système de fichiers.
Si un fichier n'existe pas, il transmet la requête au *middleware* suivant.
Si vous souhaitez retourner le message `404 File Not Found` pour les fichiers non existants,
vous pouvez utiliser la fonction [http.FileServer](https://golang.org/pkg/net/http/#FileServer)
comme `Handler`.
Exemple:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
// Exemple d'usage de la fonction http.FileServer pour avoir un comportement similaire à un
// serveur HTTP "standard" plutôt que le comportement "middleware"
// 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)
}
```
Ce programme servira les fichiers depuis le répertoire `/tmp` en premier lieu.
Si le fichier n'est pas trouvé, il transmet la requête au *middleware* suivant.
### Recupération (*Recovery*)
Ce *middleware* capture les appels à `panic` et renvoie une réponse `500` à
la requête correspondante. Si un autre *middleware* a déjà renvoyé une réponse (vide ou non),
le renvoie de la réponse `500` au client échouera, le client en ayant déjà obtenu une.
Il est possible d'adjoindre au *middleware* une fonction de type `PanicHandlerFunc`
pour collecter les erreurs `500` et les transmettre à un service de rapport d'erreur
tels Sentry ou Airbrake.
Exemple:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Ce programme renverra une erreur `500 Internal Server Error` à chaque requête reçue.
Il transmettra à son *logger* associé la trace de la pile d'exécution et affichera cette même trace sur la sortie standard si la valeur `PrintStack` est mise à `true`. (valeur par défaut)
Exemple avec l'utilisation d'une `PanicHandlerFunc`:
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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) {
// code envoyant le rapport d'erreur à Sentry
}
```
## Journalisation (*Logger*)
Ce *middleware* va *logger* toutes les requêtes et réponses.
Exemple:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Ce programme affichera un *log* similaire à celui-ci pour chaque requête/réponse:
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
Il est possible de modifier le format par défaut en utilisant la fonction `SetFormat`.
Le format est `template` dont les champs associés sont les propriétés de l'objet `LoggerEntry`.
Par exemple:
```go
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
```
Ce format proposera un affichage similaire à: `[200 18.263µs] - Go-User-Agent/1.1 `
## *Middlewares* tiers
Vous trouverez ici une liste de *middlewares* compatibles avec Negroni.
N'hésitez pas à créer une PR pour renseigner un middleware de votre cru:
| Middleware | Author | Description |
| -----------|--------|-------------|
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | Un *middleware* de gestion d'accès ACL, RBAC, ABAC basé sur [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Associez facilement les données des requêtes HTTP vers des structures Go |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | *Middleware* pour utiliser les métriques AWS cloudwatch |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | Support [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | Support [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Ajouter des délais de réponse sur les routes. Utile pour tester les effets de la latence. |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | [Agent New Relic Go](https://github.com/newrelic/go-agent) officiel |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | Agent New Relic agent pour le runtime Go |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | Compression GZIP des réponses |
| [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Middleware qui vérifie la présence d'un JWT dans le *header* `Authorization` et le décode |
| [JWT Middleware](https://github.com/mfuentesg/go-jwtmiddleware) | [Marcelo Fuentes](https://github.com/mfuentesg) | JWT middleware pour golang |
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | *Logger* basé sur Logrus |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | Middleware oAuth2 |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Générer des éléments TinySVG, HTML et CSS à la volée |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, utilisateurs et permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Créer des métriques facilement avec l'outil [prometheus](http://prometheus.io) |
| [prometheus](https://github.com/slok/go-prometheus-middleware) | [Xabier Larrakoetxea](https://github.com/slok) | [Prometheus](http://prometheus.io) Créer des métriques, avec de nombreuses options, qui suivent les standards et sont mesurées de manière efficace |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Rendre des templates JSON, XML et HTML |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Authentification sécurisée pour les APIs REST |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware implémentant des basiques de sécurité |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Gestions des sessions |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Stockez des informations à propos de votre application web (temps de réponse, etc.) |
| [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | *Middleware* d'authentification HMAC configurable basée sur [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Un *middleware* qui assigne un *header* `X-Request-Id` à chaque requête |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Un *middleware* qui gère les sessions mgo pour chaque requête (ouverture, fermeture) |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Un *middleware* qui gère l'authentification via [Twitter Digits](https://get.digits.com/) |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware qui gère les statistiques qps et la latence pour vos points de terminaison et les envoie de manière asynchrone à influx db |
| [Chaos](https://github.com/falzm/chaos) | [Marc Falzon](https://github.com/falzm) | Middleware pour injecter programmatiquement du comportement chaotique dans une application |
## Exemples
[Alexander Rødseth](https://github.com/xyproto) a créé
[mooseware](https://github.com/xyproto/mooseware), un squelette pour écrire un *middleware* Negroni.
[Prasanga Siripala](https://github.com/pjebs) a créé un squelette pour les applications web basées sur Go et Negroni: [Go-Skeleton](https://github.com/pjebs/go-skeleton)
## Rechargement automatique du code ?
[gin](https://github.com/codegangsta/gin) et
[fresh](https://github.com/pilu/fresh) permettent tous deux de recharger les applications Negroni
suite à une modification opérée dans le code.
## Lectures pour les débutants avec Go et Negroni
* [Using a Context to pass information from middleware to end handler](http://elithrar.github.io/article/map-string-interface/)
* [Understanding middleware](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
## À propos
Negroni est obsessivement développé par nulle autre personne que [Code
Gangsta](https://codegangsta.io/)
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
================================================
FILE: translations/README_ja_JP.md
================================================
# Negroni [](http://godoc.org/github.com/urfave/negroni/v3) [](https://app.wercker.com/project/bykey/13688a4a94b82d84a0b8d038c4965b61) [](https://codebeat.co/projects/github-com-urfave-negroni)
NegroniはGoによるWeb ミドルウェアへの慣用的なアプローチです。
軽量で押し付けがましい作法は無く、また`net/http`ハンドラの使用を推奨しています。
[Martini](https://github.com/go-martini/martini) の思想は気に入っているが、多くの魔法を含みすぎていると感じている方に、このNegroni はよく馴染むでしょう。
## はじめに
Goをインストールし、[GOPATH](http://golang.org/doc/code.html#GOPATH)の設定を行った後、`.go`ファイルを作りましょう。これを`server.go`とします。
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Negroni パッケージをインストールします (**NOTE**: >= **go 1.1** 以上のバージョンが必要です):
```
go get github.com/urfave/negroni/v3
```
インストールが完了したら、サーバーを起動しましょう。
```
go run server.go
```
すると、Go標準パッケージの `net/http` によるWebサーバーが`localhost:3000` で起動します。
## Negroni はWeb Application Framework ですか?
Negroni はrevel やmartini のような**フレームワークではありません**。 Negroniは `net/http`と直接結びついて動作する、ミドルウェアにフォーカスされたライブラリです。
## ルーティングの機能はありますか?
Negroni にルーティングの機能はありません。Goコミュニティには既に幾つかの優れたルーティングのライブラリが存在しており、Negroni は`net/http`と互換性のあるライブラリと協調して動作するように設計されています。例えば、[Gorilla Mux]と連携すると以下のようになります。
``` 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)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()`
`negroni.Classic()` は、多くのアプリケーションで役に立つミドルウェアを提供します
* [`negroni.Recovery`](#recovery) - Panic Recovery Middleware.
* [`negroni.Logger`](#logger) - Request/Response Logger Middleware.
* [`negroni.Static`](#static) - "public"ディレクトリの静的ファイルの処理
これらはNegroni の便利な機能を利用し始めるのをとても簡単にしてくれます。
## ハンドラ
Negroniは双方向のミドルウェアのフローを提供します。これは、`negroni.Handler` インターフェースを通じて行われます。
``` go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
ミドルウェアが既に`ResponseWriter`に書き込み処理を行っていない場合、次のミドルウェア・ハンドラを動かすために、チェーン内の次の`http.HandlerFunc`を呼び出す必要があります。
``` go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// 前処理
next(rw, r)
// 後処理
}
```
この時、`MyMiddleware`を`Use` 関数によってハンドラチェーンに割り当てることができます。
``` go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
```
また、標準パッケージに備わっている`http.Handler`をハンドラチェーンに割り当てることもできます。
``` go
n := negroni.New()
mux := http.NewServeMux()
// ルーティングの処理
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
```
## `Run()`
`Run` はアドレスの文字列を受け取り、[`http.ListenAndServe`](https://godoc.org/net/http#ListenAndServe)と同様にサーバーを起動します。
<!-- { "interrupt": true } -->
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
通常、`net/http` を使用し、ハンドラとして`Negroni`を渡すことになります。
以下により柔軟性のあるサンプルを示します。
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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
あるルーティンググループにおいて、実行する必要のあるミドルウェアがある場合、
新しいNegroni のインスタンスを作成し、ルーティングハンドラとして使用することができます。
``` go
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// admin 関連のルーティングをココに記述
// admin のミドルウェアとして、Negroni インスタンスを作成
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
```
もし[Gorilla Mux]を利用する場合、サブルーターを使うサンプルは以下の通りです。
``` go
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),
))
```
## Bundled Middleware
### Static
このミドルウェアは、ファイルシステム上のファイルをサーバーからクライアントに送信します。もし指定されたファイルが存在しない場合、次のミドルウェアにリクエストの処理を依頼します。存在しないファイルへのアクセスに対して`404 Not Found`を返したい場合、 [http.FileServer](https://golang.org/pkg/net/http/#FileServer) をハンドラとして利用すべきです。
Example:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
まず、`/tmp` ディレクトリからファイルをクライアントに送ろうとしますが、指定されたファイルがファイルシステム上に存在しない場合、プロキシは次のハンドラを呼び出します。
### Recovery
このミドルウェアは、`panic`を受け取ると、`500 internal Server Error` をレスポンスします。
もし他のミドルウェアが既に応答処理を行い、クライアントにHTTP レスポンスが帰っている場合、このミドルウェアは失敗します。
Example:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
上記のコードは、 `500 Internal Server Error` を各リクエストごとに返します。
また、スタックトレースをログに出力するだけでなく、`PrintStack`が`true`に設定されている場合、クライアントにスタックトレースを出力します。(デフォルトで`true`に設定されています。)
## Logger
このミドルウェアは、送られてきた全てのリクエストとレスポンスを記録します。
Example:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
各リクエストごとに、以下のようなログが出力されます。
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
## サードパーティ製のミドルウェア
Negroni と互換性のあるミドルウェアの一覧です。あなたが作ったミドルウェアをここに追加してもらっても構いません(PRを送って下さい)
**注意**: ここの一覧は古くなっている可能性があります。英語版のREADME.md を適宜参照して下さい。
| ミドルウェア名 | 作者 | 概要 |
| -----------|--------|-------------|
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
| [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|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
| [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 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
## 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.
## Go や Negroni の初心者にオススメの参考資料(英語)
* [Using a Context to pass information from middleware to end handler](http://elithrar.github.io/article/map-string-interface/)
* [Understanding middleware](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
## Negroni について
Negroni は他ならぬ[Code
Gangsta](https://codegangsta.io/)によって異常なまでにデザインされた素晴らしいライブラリです。
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
================================================
FILE: translations/README_ko_KR.md
================================================
# Negroni
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/negroni)
**공지:** 이 라이브러리는 아래의 주소로 많이 알려져 왔습니다.
`github.com/codegangsta/negroni` -- Github가 자동으로 이 저장소에 대한 요청을 리다이렉트 시킬 것이지만, 확실한 사용을 위해 참조를 이곳으로 변경하는 것을 추천드립니다.
Negroni는 Go에서 웹 미들웨어로의 자연스러운 접근을 추구합니다. 이것은 작고, 거슬리지 않으며 `net/http` 핸들러의 사용을 지향하는 것을 의미합니다.
만약 당신이 [Martini](https://github.com/go-martini/martini)의 기본적인 컨셉을 원하지만, 이것이 너무 많은 기능을 포함한다고 느껴졌다면 Negroni가 최적의 선택일 것입니다.
## 시작하기
Go 설치와 [GOPATH](http://golang.org/doc/code.html#GOPATH)를 세팅하는 작업을 완료한 뒤, 당신의 첫 `.go` 파일을 생성하세요.
우리는 이를 `server.go` 라고 부를 것입니다.
<!-- { "interrupt": true } -->
```go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
그리고 Negroni 패키지를 설치합니다 (**공지**: **go 1.1** 이상이 요구됩니다) :
```
go get github.com/urfave/negroni/v3
```
서버를 실행시킵니다:
```
go run server.go
```
이제 `localhost:3000`에서 동작하는 Go `net/http` 웹서버를 가지게 되었습니다.
### 패키징 (*Packaging*)
Debian을 사용중이시라면, `negroni`는 [a package](https://packages.debian.org/sid/golang-github-urfave-negroni-dev)에서도 사용이 가능합니다. `apt install golang-github-urfave-negroni-dev`를 통해서 설치가 가능합니다. (글을 작성할 당시, 이는 `sid` 저장소 안에 있습니다.)
## Negroni는 프레임워크(*Framework*)인가요?
Negroni는 프레임워크가 **아닙니다.** 이는 `net/http`를 직접적으로 이용할 수 있도록 디자인된 미들웨어 중심의 라이브러리입니다.
## 라우팅(*Routing*) ?
Negroni는 *BYOR* (*Bring your own Router*, 나의 라우터를 사용하기)를 지향합니다. Go 커뮤니티에는 이미 좋은 http 라우터들이 존재하기 때문에 Negroni는 그들과 잘 어우러질 수 있도록 `net/http`를 전적으로 지원하고 있습니다.
예를 들어, 아래는 [Gorilla Mux]를 사용한 예입니다:
```go
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
n := negroni.New(Middleware1, Middleware2)
// Use() 함수를 사용해서 미들웨어를 사용할수도 있습니다.
n.Use(Middleware3)
// 라우터의 경우 마지막에 옵니다.
n.UseHandler(router)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()`
`negroni.Classic()`은 대부분의 어플리케이션에서 유용하게 사용되는 기본적인 미들웨어들을 제공합니다.
- [`negroni.Recovery`](#recovery) - Panic 복구용 미들웨어
- [`negroni.Logger`](#logger) - Request/Response 로깅 미들웨어
- [`negroni.Static`](#static) - "public" 디렉터리 아래의 정적 파일 제공(*serving*)을 위한 미들웨어
이는 Negroni의 유용한 기능들을 사용하기 시작하는데 큰 도움이 되도록 만들어줄 것입니다.
## 핸들러(*Handlers*)
Negroni는 양방향 미들웨어 흐름을 제공합니다. 이는 `negroni.Handler` 인터페이스를 통해 구현합니다.
```go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
미들웨어가 `ResponseWriter`에 아직 무언가 쓰지 않았다면, 이는 다음 미들웨어에 연결되어있는 `http.HandleFunc`를 호출해야 합니다. 이는 유용하게 사용될 수 있습니다:
```go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// next() 처리 이전 작업 수행
next(rw, r)
// next() 처리 이후 작업 수행
}
```
이후 `Use` 함수를 통해 핸들러 체인(*handler chain*)에 매핑 시킬 수 있습니다:
```go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
```
또한, 기존의 `http.Handler`들과도 매핑시킬 수 있습니다:
```go
n := negroni.New()
mux := http.NewServeMux()
// 여기에 라우트들을 매핑하세요
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
```
## `With()`
Negroni는 `With`라고 불리는 편리한 함수를 가지고 있습니다. `With`는 한 개 혹은 그 이상의 `Handler` 인스턴스들을 받아 기존 리시버의 핸들러들과 새로운 핸들러들이 조합된 새로운 `Negroni` 객체를 리턴합니다.
```go
// 재사용을 원하는 미들웨어들
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)
// `specific`은 `common`의 핸들러들과 새로 전달된 핸들러들이 조합된 새로운 `negroni` 객체
specific := common.With(
SpecificMiddleware1,
SpecificMiddleware2
)
```
## `Run()`
Negroni는 `Run`이라고 불리는 편리한 함수를 가지고 있습니다. `Run`은 `http.ListenAndServe`와 같이 주소 스트링 값(*addr string*)을 넘겨받습니다.
<!-- { "interrupt": true } -->
```go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
만약 주소 값이 제공되지 않는다면, `PORT` 환경 변수가 대신 사용됩니다. `PORT` 환경 변수 또한 정의되어있지 않다면, 기본 주소(*default address*)가 사용됩니다. 전체 설명을 보시려면 [Run](https://godoc.org/github.com/urfave/negroni/v3#Negroni.Run)을 참고하세요.
일반적으로는, 좀 더 유연한 사용을 위해서 `net/http` 메서드를 사용하여 `negroni` 객체를 핸들러로서 넘기는 것을 선호할 것입니다. 예를 들면:
<!-- { "interrupt": true } -->
```go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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*)
만약 당신이 라우트들의 라우트 그룹을 가지고 있다면 그런데 그것은 실행되어야하는 미들웨어를 필요로한다.
특정 라우트 그룹만이 사용하는 미들웨어가 있다면, 간단하게 Negroni 인스턴스를 새롭게 생성하여 라우트 핸들러(*route handler*)로서 사용하면 된다.
```go
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// admin routes를 여기에 추가하세요
// 관리자 미들웨어들을 위한 새로운 negroni 인스턴스를 생성합니다
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
```
[Gorilla Mux]를 사용하고 있다면, 아래는 서브 라우터(subrouter)를 사용하는 예제입니다:
```go
router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"
// "/subpath" 는 subRouter와 메인 라우터(main router)의 연결 보장을 위해 반드시 필요합니다
router.PathPrefix("/subpath").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(subRouter),
))
```
`With()`는 라우터 간 공유시 발생하는 미들웨어 중복을 방지하기 위해서 사용될 수 있습니다.
```go
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// api 라우트들을 여기에 추가하세요
webRoutes := mux.NewRouter()
// web 라우트들을 여기에 추가하세요
// 라우터간 공유될 common 미들웨어를 생성합니다
common := negroni.New(
Middleware1,
Middleware2,
)
// common 미들웨어를 기반으로
// api 미들웨어를 위한 새로운 negroni 객체를 생성합니다
router.PathPrefix("/api").Handler(common.With(
APIMiddleware1,
negroni.Wrap(apiRoutes),
))
// common 미들웨어를 기반으로
// web 미들웨어를 위한 새로운 negroni 객체를 생성합니다
router.PathPrefix("/web").Handler(common.With(
WebMiddleware1,
negroni.Wrap(webRoutes),
))
```
## 번들 미들웨어(*Bundled Middleware*)
### Static
이 미들웨어는 파일들을 파일 시스템(*filesystem*)으로 제공하는 역할을 수행합니다. 파일이 존재하지 않을 경우, 요청을 다음 미들웨어로 넘깁니다. 존재하지 않는 파일에 대해 `404 File Not Found`를 유저에게 반환하길 원하는 경우 [http.FileServer](https://golang.org/pkg/net/http/#FileServer)를 핸들러로 사용하는 것을 살펴보아야 합니다.
예제:
<!-- { "interrupt": true } -->
```go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
// "미들웨어(middleware)"의 역할보다는 "서버와 같은(server-like)" 역할을 수행하기를 원할 때
// http.FileServer를 사용한 예제
// 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)
}
```
위 코드는 `/tmp` 디렉터리로부터 파일을 제공할 것입니다. 그러나 파일 시스템 상의 파일에 대한 요청이 일치하지 않는 경우 프록시는 다음 핸들러를 호출할 것입니다.
### Recovery
이 미들웨어는 `panic`들을 감지하고 `500` 응답 코드(*response code*)를 반환하는 역할을 수행합니다. 다른 미들웨어가 응답 코드 또는 바디(*body*)를 쓸 경우, 클라이언트는 이미 HTTP 응답 코드를 받았기 때문에 이 미들웨어가 적절한 시점에 `500` 코드를 보내는 것에 실패할 것입니다. 추가적으로 `PanicHandlerFunc`는 Sentry 또는 Aribrake와 같은 에러 보고 서비스에 `500` 코드를 반환하도록 붙을 수 있습니다.
예제:
<!-- { "interrupt": true } -->
```go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
위 코드는 각 요청에 대해 `500 Internal Server Error` 반환할 것입니다. `PrintStack` 값이 `true` (기본 값)로 설정되어있다면 요청자(*requester*)에게 스택 트레이스(*stack trace*) 값을 출력하는 것처럼 로깅 또한 진행합니다.
`PanicHandlerFunc`를 사용한 예제:
```go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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) {
// Sentry에게 에러를 보고하는 코드를 작성하세요
}
```
미들웨어는 `STDOUT`에 기본으로 값들을 출력합니다. 하지만 `SetFormatter()` 함수를 이용해서 출력 프로세스를 커스터마이징 할 수 있습니다. 당연히 `HTMLPanicFormatter`를 사용해서 깔끔한 HTML로도 에러 상황을 보여줄 수 있습니다.
<!-- { "interrupt": true } -->
```go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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
이 미들웨어는 서버에 들어오는 요청과 응답들을 기록하는 역할을 수행합니다.
예제:
<!-- { "interrupt": true } -->
```go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
위 코드는 각 요청에 대해 아래와 같이 출력할 것입니다.
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
물론, `SetFormat` 함수를 이용해 사용자만의 로그 포맷(*log format*) 또한 정의할 수 있습니다. 로그 포맷은 `LoggerEntry` 구조체 내부의 필드들로 구성된 템플릿 문자열입니다.
```go
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
```
위 구조는 이와 같이 출력될 것입니다 - `[200 18.263µs] - Go-User-Agent/1.1 `
## Third Party Middleware
아래는 현재(2018.10.10) Negroni와 호환되는 미들웨어들입니다. 당신의 미들웨어가 링크되기를 원한다면 자유롭게 PR을 보내주세요.
| Middleware | Author | Description |
| ------------------------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------ |
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
| [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 |
| [JWT Middleware](https://github.com/mfuentesg/go-jwtmiddleware) | [Marcelo Fuentes](https://github.com/mfuentesg) | JWT middleware for golang |
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
| [prometheus](https://github.com/slok/go-prometheus-middleware) | [Xabier Larrakoetxea](https://github.com/slok) | [Prometheus](http://prometheus.io) metrics with multiple options that follow standards and try to be measured in a efficent way |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
| [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 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
| [Chaos](https://github.com/falzm/chaos) | [Marc Falzon](https://github.com/falzm) | Middleware for injecting chaotic behavior into application in a programmatic way |
## 예제
[Alexander Rødseth](https://github.com/xyproto)는 Negroni 미들웨어 핸들러를 작성하기 위한 뼈대인 [mooseware](https://github.com/xyproto/mooseware)를 만들었습니다.
[Prasanga Siripala](https://github.com/pjebs)는 웹 기반의 Go/Negroni 프로젝트들을 위한 효율적인 뼈대 구조를 만들었습니다 : [Go-Skeleton](https://github.com/pjebs/go-skeleton)
## 코드 실시간 새로고침(Live code reload)?
[gin](https://github.com/codegangsta/gin)과 [fresh](https://github.com/pilu/fresh) 모두 negroni 앱의 실시간 새로고침(*live reload*)을 지원합니다.
## Go & Negroni 초심자들이 필수적으로 읽어야하는 자료들
- [Using a Context to pass information from middleware to end handler](http://elithrar.github.io/article/map-string-interface/)
- [Understanding middleware](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
## 추가 정보
Negroni는 [Code Gangsta](https://codegangsta.io/)에 의해 디자인 되었습니다.
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
================================================
FILE: translations/README_pt_br.md
================================================
# Negroni
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/negroni)
**Notice:** Esta é uma biblioteca conhecida anteriormente como
`github.com/codegangsta/negroni` -- Github irá redirecionar automaticamente as requisições
para este repositório, mas recomendamos atualizar suas referências por clareza.
O Negroni é uma abordagem idiomática para middlewares web em Go. É pequeno, não intrusivo, e incentiva o 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.
Idiomas traduzidos:
* [Deutsch (de_DE)](./README_de_de.md)
* [Português Brasileiro (pt_BR)](./README_pt_br.md)
* [简体中文 (zh_cn)](./README_zh_cn.md)
* [繁體中文 (zh_tw)](./README_zh_tw.md)
* [日本語 (ja_JP)](./README_ja_JP.md)
## Começando
Depois de instalar a linguagem Go e definir seu [GOPATH](http://golang.org/doc/code.html#GOPATH), crie seu primeiro arquivo `.go`.
Chamaremos ele de `server.go`.
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Depois instale o pacote Negroni (**go 1.1** ou superior)
```
go get github.com/urfave/negroni/v3
```
Em seguida, execute seu servidor:
```
go run server.go
```
Agora você tem um servidor web Go net/http rodando em `localhost:3000`.
## Empacotamento
Se você está no Debian, `negroni` também está disponível como [um pacote](https://packages.debian.org/sid/golang-github-urfave-negroni-dev) que
você pode instalar via `apt install golang-github-urfave-negroni-dev` (no momento que isto foi escrito, ele estava em repositórios do `sid`).
## 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)
http.ListenAndServe(":3000", n)
```
## `With()`
Negroni tem uma conveniente função chamada `With`. `With` pega uma ou mais
instâncias `Handler` e retorna um novo `Negroni` com a combinação dos handlers de receivers e os novos handlers.
```go
// 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 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).
<!-- { "interrupt": true } -->
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
Se nenhum endereço for fornecido, a variável de ambiente `PORT` é usada no lugar.
Se a variável de ambiente `PORT` não for definida, o endereço padrão será usado.
Veja [Run](https://godoc.org/github.com/urfave/negroni/v3#Negroni.Run) para uma descrição completa.
No geral, você irá quere usar métodos `net/http` e passar `negroni` como um `Handler`,
como ele é mais flexível, e.g.:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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())
}
```
## 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
// Create a new negroni for the admin middleware
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
```
Se você está usando [Gorilla Mux], aqui é um exemplo usando uma subrota:
``` go
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()` pode ser usado para eliminar redundância por middlewares compartilhados através de rotas.
``` go
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
Este middleware servirá arquivos do filesystem. Se os arquivos não existerem,
ele substituirá a requisição para o próximo middleware. Se você quer que as
requisições para arquivos não existentes retorne um `404 File Not Found` para o user
você deve olhar em usando
[http.FileServer](https://golang.org/pkg/net/http/#FileServer) como um handler.
Exemplo:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Serviá arquivos do diretório `/tmp` primeiro, mas substituirá chamadas para o
próximo handler se a requisição não encontrar um arquivo no filesystem.
### Recovery
Este middleware pega repostas de `panic` com um código de resposta `500`. Se
algum outro middleware escreveu um código resposta ou conteúdo, este middleware
falhará corretamente enviando um 500 para o cliente, como o cliente já recebeu
o código de resposta HTTP. Adicionalmente um `PanicHandlerFunc` pode ser anexado
para reportar 500 para um serviço de report como um Sentry ou Airbrake.
Exemplo:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Irá retornar um `500 Internal Server Error` para cada requisição. Ele também irá
registrar stack trace bem como um print na stack tarce para quem fez a requisição
if `PrintStack` estiver setado como `true` (o padrão).
Exemplo com handler de erro:
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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
}
```
## Logger
Este middleware loga cada entrada de requisição e resposta.
Exemplo:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
Irá printar um log simular para:
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
em cada request.
Você também pode definir seu próprio formato de log chamando a função `SetFormat`.
O formato é uma string de template como os campos como mencionados na struct `LoggerEntry`.
Então, como exemplo -
```go
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
```
Mostrará algo como - `[200 18.263µs] - Go-User-Agent/1.1 `
## 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 |
| -----------|--------|-------------|
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | ACL, RBAC, ABAC Authorization middlware based on [Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | Graceful HTTP Shutdown |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP response compression |
| [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|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 middleware |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session Management |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) |
| [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 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | Middleware that handles creating and closing mgo sessions per request |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
## Exemplos
[Alexander Rødseth](https://github.com/xyproto) criou [mooseware](https://github.com/xyproto/mooseware), uma estrutura para escrever um handler middleware Negroni.
[Prasanga Siripala](https://github.com/pjebs) Criou um efetivo estrutura esqueleto para projetos Go/Negroni baseados na web: [Go-Skeleton](https://github.com/pjebs/go-skeleton)
## 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/)
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
================================================
FILE: translations/README_zh_CN.md
================================================
# Negroni
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/negroni)
**注意:** 本函数库原本位于 `github.com/codegangsta/negroni` -- Github 会自动将请求重定向到当前地址, 但我们建议你更新一下引用路径。
在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它轻量且非侵入,鼓励使用原生 `net/http` 处理器(Handlers)。
如果你喜欢用 [Martini](http://github.com/go-martini/martini) ,但又觉得它太魔幻,那么 Negroni 就是个很好的选择了。
各国语言翻译:
* [德语 (de_DE)](./README_de_de.md)
* [葡萄牙语 (pt_BR)](./README_pt_br.md)
* [简体中文 (zh_CN)](./README_zh_CN.md)
* [繁體中文 (zh_tw)](./README_zh_tw.md)
* [日本語 (ja_JP)](./README_ja_JP.md)
* [法语 (fr_FR)](./README_fr_FR.md)
* [韩语 (ko_KR)](./README_ko_KR.md)
## 入门指导
在安装了 Go 语言并设置好了 [GOPATH](http://golang.org/doc/code.html#GOPATH) 后,新建第一个 `.go` 文件,命名为 `server.go`。
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
然后安装 Negroni 包(**注意**:要求 **Go 1.1** 或更高的版本的 Go 语言环境):
```
go get github.com/urfave/negroni/v3
```
最后运行刚建好的 server.go 文件:
```
go run server.go
```
这时一个 Go `net/http` Web 服务器会跑在 `localhost:3000` 上,使用浏览器打开 `localhost:3000` 可看到输出的结果。
### 第三方包
如果你使用 Debian 系统,你可以执行 `apt install golang-github-urfave-negroni-dev` 来安装 `negroni`。 [包地址](https://packages.debian.org/sid/golang-github-urfave-negroni-dev) (写该文档时,它是在 `sid` 仓库中).
## Negroni 是一个框架吗?
Negroni **不**是一个框架,它是为了方便使用 `net/http` 而设计的一个库而已。
## 路由呢?
Negroni 没有带路由功能,因此使用 Negroni 时,需要有一个适合你的路由。不过好在 Go 社区里已经有相当多好用的路由,Negroni 和那些完全支持 `net/http` 库的路由搭配使用更佳,比如搭配 [Gorilla Mux](http://github.com/gorilla/mux) 路由,可以这样使用:
``` 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)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()` 经典的实例
`negroni.Classic()` 提供一些默认的中间件,这些中间件在多数应用都很有用。
* `negroni.Recovery` - 异常(恐慌)恢复中间件
* `negroni.Logging` - 请求 / 响应的日志中间件
* `negroni.Static` - 静态文件处理中间件,默认目录为 "public"
`negroni.Classic()` 使你可以轻松上手 Negroni 那些有用的特性。
## Handlers (处理器)
Negroni 提供双向的中间件机制,通过 `negroni.Handler` 这个接口来实现:
``` go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
在中间件尚未写入 `ResponseWriter` 时,它会调用链上的下一个 `http.HandlerFunc` 以执行下一个中间件处理器。以下代码就是优雅的使用方式:
``` go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// do some stuff before
next(rw, r)
// do some stuff after
}
```
你也可以用 `Use` 函数把它映射到处理器链上:
``` go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
```
你也可以映射 `http.Handler`。
``` go
n := negroni.New()
mux := http.NewServeMux()
// map your routes
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
```
## `With()`
Negroni 有一个便利的函数叫 `With`。 `With` 函数可以把一个或多个 `Handler` 实例和接收者处理器集合组合成新的处理器集合,并返回新的 `Negroni` 实例对象。
```go
// 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 有一个便利的函数叫 `Run`。 `Run` 接收 addr 地址字符串 [http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe).
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
未提供地址的情况下, `PORT` 系统环境变量会被使用。 若未定义该系统环境变量,默认的地址则会被使用。请参考 [Run](https://godoc.org/github.com/urfave/negroni/v3#Negroni.Run) 的详情说明。
一般来说,使用 `net/http` 方法并将 Negroni 当作处理器传入,这样更灵活,例如:
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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())
}
```
## 特定路由(分组路由)
如果你有一组需要执行特定中间件的路由,你可以新建一个 Negroni 实例,然后把它当作你的路由处理器即可。
``` go
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),
))
```
如果你使用 [Gorilla Mux](https://github.com/gorilla/mux),下面是一个使用 subrouter 的例子:
``` go
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()` 可被用来减少中间件在跨路由共享时的的冗余.
``` go
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),
))
```
## 内置中间件
### 静态文件处理
该中间件通过文件系统来代理(处理)静态文件。 一旦文件不存在,请求代理会转到下个中间件。如果你想要处理不存在的文件返回 `404 File Not Found` HTTP 错误,请参考使用 [http.FileServer](https://golang.org/pkg/net/http/#FileServer) 作为处理器吧。
例子:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
中间件首先在 `/tmp` 找文件,一旦在文件系统中找不到匹配的文件,代理将调用下一个处理器。
### 恢复
该中间件捕捉 `panic` 并返回错误代码为 `500` 的响应。如果其他中间件写了响应代码或 Body 内容的话,该中间件会无法顺利地传送 500 错误给客户端,因为客户端已经收到 HTTP 响应代码。另外,可以附上 `PanicHandlerFunc` 来报 500 错误给错误报告系统,如 Sentry 或 Airbrake。
例子:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
它将输出 `500 Internal Server Error` 给每个请求。如果 `PrintStack` 设成 `true` (默认值)的话,它也会把错误日志写入请求方追踪堆栈。
加错误处理器的例子:
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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) {
// 在这写些程式回报错误给 Sentry
}
```
默认情况下,这个中间件会简要输出日志信息到 STDOUT 上。当然你也可以通过 `SetFormatter()` 函数自定义输出的日志。
当发生崩溃时,同样你也可以通过 `HTMLPanicFormatter` 来显示美化的 HTML 输出结果。
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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
该中间件负责打印各个请求和响应日志。
例子:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
每个请求打印日志将如下所示:
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
你也可以调用 `SetFormat` 函数来自定义日志的格式。格式是一个预定义了字段的 `LoggerEntry` 结构体。如下所示:
```go
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
```
会输出:`[200 18.263µs] - Go-User-Agent/1.1 `。
## 第三方兼容中间件
以下是兼容 Negroni 的中间件列表,如果你也有兼容 Negroni 的中间件,如果想提交自己的中间件,建议你附上 PR 链接。
| 中间件 | 作者 | 描述 |
| ---------------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | 支持ACL, RBAC, ABAC的权限管理中间件,基于[Casbin](https://github.com/casbin/casbin) |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | HTTP 请求数据注入到 structs 实体 |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS CloudWatch 矩阵的中间件 |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | 基于[Content Security Policy](https://www.w3.org/TR/CSP2/)(CSP) |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | 为endpoints增加延迟时间. 在测试严重网路延迟的效应时好用 |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | 官网 [New Relic Go Agent](https://github.com/newrelic/go-agent) (目前正在测试阶段) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/stretchr/graceful) | [Tyler Bunnell](https://github.com/tylerb) | 优雅关闭 HTTP 的中间件 |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | 响应流 GZIP 压缩 |
| [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 |
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | 基于 Logrus-based logger 日志 |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2 中间件 |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | 快速生成 TinySVG, HTML and CSS 中间件 |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, 用户和权限 |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | 简易建立矩阵端点给[prometheus](http://prometheus.io)建构工具 |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | 渲染 JSON, XML and HTML 中间件 |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | REST API 接口的安全认证 |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session 会话管理 |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | 检测 web 应用当前运行状态信息 (响应时间等等。) |
| [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 鉴权认证的中间件 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | 给每个请求指定一个随机 X-Request-Id 头的中间件 |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | 处理在每个请求建立与关闭 mgo sessions |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | 处理 [Twitter Digits](https://get.digits.com/) 的认证 |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | endpoints用的管理QPS与延迟状态的中间件非同步地将状态刷入InfluxDB |
| [Chaos](https://github.com/falzm/chaos) | [Marc Falzon](https://github.com/falzm) | 以编程方式在应用程式中插入无序行为的中间件 |
## 范例
[Alexander Rødseth](https://github.com/xyproto) 创建的 [mooseware](https://github.com/xyproto/mooseware) 是一个编写兼容 Negroni 中间件处理器的骨架。
[Prasanga Siripala](https://github.com/pjebs) 创建的 [Go-Skeleton](https://github.com/pjebs/go-skeleton) 是一个高效编写基于 web 的 Go/Negroni 项目的骨架。
## 即时编译
[gin](https://github.com/codegangsta/gin) 和 [fresh](https://github.com/pilu/fresh) 这两个应用是即时编译的 Negroni 工具。
## Go & Negroni 初学者必读推荐
* [使用上下文把消息从中间件传递给后端处理器](http://elithrar.github.io/article/map-string-interface/)
* [理解中间件](http://mattstauffer.co/blog/laravel-5.0-middleware-replacing-filters)
## 关于 Negroni
Negroni 原由 [Code Gangsta](https://codegangsta.io/) 主导设计开发。
================================================
FILE: translations/README_zh_tw.md
================================================
# Negroni(尼格龍尼)
[](http://godoc.org/github.com/urfave/negroni/v3)
[](https://travis-ci.org/urfave/negroni)
[](https://codebeat.co/projects/github-com-urfave-negroni)
[](https://codecov.io/gh/urfave/negroni)
**注意:** 本函式庫原來自於
`github.com/codegangsta/negroni` -- Github會自動將連線轉到本連結, 但我們建議你更新一下參照.
尼格龍尼是一款web中介器. 道地的Go寫法、精簡、非侵入、鼓勵用`net/http`處理器.
如果你喜歡[Martini](http://github.com/go-martini/martini), 但覺得這其中包太多神奇的功能, 那麼尼格龍尼會是你的最佳選擇.
其他語言:
* [Deutsch (de_DE)](./README_de_de.md)
* [Português Brasileiro (pt_BR)](./README_pt_br.md)
* [简体中文 (zh_cn)](./README_zh_CN.md)
* [繁體中文 (zh_tw)](./README_zh_tw.md)
* [日本語 (ja_JP)](./README_ja_JP.md)
* [Français (fr_FR)](./README_fr_FR.md)
## 入門
安裝完Go且設好[GOPATH](http://golang.org/doc/code.html#GOPATH), 建立你的第一個`.go`檔. 可以命名為`server.go`.
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
安裝尼格龍尼套件 (最低需求為**go 1.1**或更高版本):
```
go get github.com/urfave/negroni/v3
```
執行伺服器:
```
go run server.go
```
你現在起了一個Go的net/http網頁伺服器在`localhost:3000`.
### 打包
如果`negroni`在Debian環境下是個[套件](https://packages.debian.org/sid/golang-github-urfave-negroni-dev), 可直接
執行`apt install golang-github-urfave-negroni-dev`安裝(這在`sid`倉庫中).
## 尼格龍尼是個framework嗎?
尼格龍尼**不是**framework, 是個設計用來直接使用net/http的library.
## 路由?
尼格龍尼是BYOR (Bring your own Router, 帶給你自訂路由). 在Go社群已經有大量可用的http路由器, 尼格龍尼試著做好完全支援`net/http`, 例如與[Gorilla Mux](http://github.com/gorilla/mux)整合:
``` go
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
n := negroni.New(Middleware1, Middleware2)
// 或在Use()函式中使用中介器
n.Use(Middleware3)
// 路由器放最後
n.UseHandler(router)
http.ListenAndServe(":3001", n)
```
## `negroni.Classic()`
`negroni.Classic()` 提供一些好用的預設中介器:
* [`negroni.Recovery`](https://github.com/urfave/negroni#recovery) - Panic 還原中介器
* [`negroni.Logging`](https://github.com/urfave/negroni#logger) - Request/Response 紀錄中介器
* [`negroni.Static`](https://github.com/urfave/negroni#static) - 在"public"目錄下的靜態檔案服務
尼格龍尼的這些功能讓你開發變得很簡單.
## 處理器(Handlers)
尼格龍尼提供一個雙向中介器的機制, 介面為`negroni.Handler`:
``` go
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
如果中介器沒有寫入ResponseWriter, 會呼叫通道裡面的下個`http.HandlerFunc`讓給中介處理器. 可以被用來做良好的應用:
``` go
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// 在這之前做一些事
next(rw, r)
// 在這之後做一些事
}
```
然後你可以透過 `Use` 函數對應到處理器的通道:
``` go
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
```
你也可以對應原始的 `http.Handler`:
``` go
n := negroni.New()
mux := http.NewServeMux()
// map your routes
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
```
## `Run()`
尼格龍尼有一個很好用的函數`Run`, `Run`接收addr字串辨識[http.ListenAndServe](http://golang.org/pkg/net/http#ListenAndServe).
``` go
package main
import (
"github.com/urfave/negroni/v3"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
```
未提供路徑情況下會使用系統環境變數`PORT`, 若未定義該系統環境變數則會用預設路徑, 請見[Run](https://godoc.org/github.com/urfave/negroni/v3#Negroni.Run)細看說明.
一般來說, 你會希望使用 `net/http` 方法, 並且將尼格龍尼當作處理器傳入, 這相對起來彈性比較大, 例如:
``` go
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/urfave/negroni/v3"
)
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())
}
```
## 路由特有中介器
如果你有一群路由需要執行特別的中介器, 你可以簡單的建立一個新的尼格龍尼實體當作路由處理器.
``` go
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// 在這裡新增管理用的路由
// 為管理中介器建立一個新的尼格龍尼
router.Handle("/admin", negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
```
如果你使用 [Gorilla Mux](https://github.com/gorilla/mux), 下方是一個使用 subrounter 的例子:
``` go
router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"
// "/subpath" 是用來保證subRouter與主要路由連結的必要參數
router.PathPrefix("/subpath").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(subRouter),
))
```
`With()` 可被用來降低在跨路由分享時多餘的中介器.
``` go
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// 在此新增API路由
webRoutes := mux.NewRouter()
// 在此新增Web路由
// 建立通用中介器來跨路由分享
common := negroni.New(
Middleware1,
Middleware2,
)
// 為API中介器建立新的negroni
// 使用通用中介器作底
router.PathPrefix("/api").Handler(common.With(
APIMiddleware1,
negroni.Wrap(apiRoutes),
))
// 為Web中介器建立新的negroni
// 使用通用中介器作底
router.PathPrefix("/web").Handler(common.With(
WebMiddleware1,
negroni.Wrap(webRoutes),
))
```
## 內建中介器
### 靜態
本中介器會在檔案系統上服務檔案. 若檔案不存在, 會將流量導(proxy)到下個中介器.
如果你想要返回`404 File Not Found`給檔案不存在的請求, 請使用[http.FileServer](https://golang.org/pkg/net/http/#FileServer)
作為處理器.
範例:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
// http.FileServer的使用範例, 若你預期要"像伺服器"而非"中介器"的行為
// 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)
}
```
從`/tmp`目錄開始服務檔案 但如果請求的檔案在檔案系統中不符合, 代理會
呼叫下個處理器.
### 恢復
本中介器接收`panic`跟錯誤代碼`500`的回應. 如果其他任何中介器寫了回應
的HTTP代碼或內容的話, 中介器會無法順利地傳送500給用戶端, 因為用戶端
已經收到HTTP的回應代碼. 另外, 可以掛載`PanicHandlerFunc`來回報500
的錯誤到錯誤回報系統, 如: Sentry或Airbrake.
範例:
<!-- { "interrupt": true } -->
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
將回傳`500 Internal Server Error`到每個結果. 也會把結果紀錄到堆疊追蹤,
`PrintStack`設成`true`(預設值)的話也會印到註冊者.
加錯誤處理器的範例:
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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) {
// 在這寫些程式回報錯誤給Sentry
}
```
中介器在預設會簡易的輸出資訊到STDOUT. 你可以使用`SetFormatter()`函式客製化輸出的程序.
你也可以使用`HTMLPanicFormatter` 在錯誤時顯示格式化的HTML.
``` go
package main
import (
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
## 記錄器
本中介器紀錄各個進入的請求與回應.
範例:
<!-- { "interrupt": true } -->
``` go
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni/v3"
)
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)
}
```
在每個請求印的紀錄會看起來像:
```
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
```
你也可以用`SetFormat`函式來 設定自己的紀錄格式. 格式樣本字串與欄位如`LoggerEntry`結構中所述. 例:
```go
l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
```
會顯示像是 - `[200 18.263µs] - Go-User-Agent/1.1 `
## 第三方中介器
以下清單是目前可用於尼格龍尼的中介器. 如果你自己手癢做了一個, 請別吝嗇自己把連結貼在下面吧:
| 中介器 | 作者 | 說明 |
| -----------|--------|-------------|
| [authz](https://github.com/casbin/negroni-authz) | [Yang Luo](https://github.com/hsluoyz) | 一款使用[Casbin](https://github.com/casbin/casbin)的權限管理中介器可支援ACL, RBAC, ABAC |
| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | 把HTTP請求的資料榜定到structs |
| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS CloudWatch 矩陣的中介器 |
| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | 支援[Cross Origin Resource Sharing](http://www.w3.org/TR/cors/)(CORS) |
| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | 支援[Content Security Policy](https://www.w3.org/TR/CSP2/)(CSP) |
| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | 為endpoints增加延遲時間. 在測試嚴重網路延遲的效應時好用 |
| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | 官網 [New Relic Go Agent](https://github.com/newrelic/go-agent) (目前正在測試階段) |
| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime |
| [Graceful](https://github.com/tylerb/graceful) | [Tyler Bunnell](https://github.com/tylerb) | 優雅地關閉HTTP |
| [gzip](https://github.com/phyber/negroni-gzip) | [phyber](https://github.com/phyber) | GZIP資源壓縮 |
| [JWT Middleware](https://github.com/auth0/go-jwt-middleware) | [Auth0](https://github.com/auth0) | Middleware 檢查JWT在`Authorization` header on incoming requests and decodes it|
| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | 基於Logrus的紀錄器 |
| [oauth2](https://github.com/goincremental/negroni-oauth2) | [David Bochenski](https://github.com/bochenski) | oAuth2中介器 |
| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | 一秒產生TinySVG, HTML, CSS |
| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies與使用者權限配套 |
| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | 簡易建立矩陣端點給[prometheus](http://prometheus.io)建構工具 |
| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | JSON, XML, HTML樣板的渲染 |
| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | REST API端點安全認證 |
| [secure](https://github.com/unrolled/secure) | [Cory Jacobsen](https://github.com/unrolled) | 簡易安全中介器 |
| [sessions](https://github.com/goincremental/negroni-sessions) | [David Bochenski](https://github.com/bochenski) | Session 管理 |
| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | 儲存關於網頁應用的資訊(回應時間之類) |
| [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | 可設定的[AWS風格](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC認證中介器 |
| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | 在每個request指定一個隨機X-Request-Id header的中介器 |
| [mgo session](https://github.com/joeljames/nigroni-mgo-session) | [Joel James](https://github.com/joeljames) | 處理在每個請求建立與關閉mgo sessions |
| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | 處理[Twitter Digits](https://get.digits.com/)的認證 |
| [stats](https://github.com/guptachirag/stats) | [Chirag Gupta](https://github.com/guptachirag/stats) | 終端用的管理QPS與延遲狀態的中介器非同步地將狀態刷入InfluxDB |
| [Chaos](https://github.com/falzm/chaos) | [Marc Falzon](https://github.com/falzm) | 以開發的方式在應用程式中插入無序行為的中介器 |
## 應用範例
[Alexander Rødseth](https://github.com/xyproto)所建
[mooseware](https://github.com/xyproto/mooseware)用來寫尼格龍尼中介處理器的骨架
[Go-Skeleton](https://github.com/pjebs/go-skeleton)有效的網頁GO尼格龍尼骨架
## 即時編譯
[gin](https://github.com/codegangsta/gin)和
[fresh](https://github.com/pilu/fresh)兩個尼格龍尼即時重載的應用.
## Go & 尼格龍尼初學者必讀
* [使用Context將資訊從中介器送到處理器](http://elithrar.github.io/article/map-string-interface/)
* [理解中介器](https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style)
## 關於
尼格龍尼正是[Code Gangsta](https://codegangsta.io/)的執著設計.
[Gorilla Mux]: https://github.com/gorilla/mux
[`http.FileSystem`]: https://godoc.org/net/http#FileSystem
譯者: Festum Qin (Festum@G.PL)
gitextract_j1egrmu8/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc.go
├── go.mod
├── logger.go
├── logger_test.go
├── negroni.go
├── negroni_bench_test.go
├── negroni_test.go
├── recovery.go
├── recovery_test.go
├── response_writer.go
├── response_writer_feature.go
├── response_writer_pusher.go
├── response_writer_pusher_test.go
├── response_writer_test.go
├── static.go
├── static_test.go
└── translations/
├── README_de_de.md
├── README_fr_FR.md
├── README_ja_JP.md
├── README_ko_KR.md
├── README_pt_br.md
├── README_zh_CN.md
└── README_zh_tw.md
SYMBOL INDEX (147 symbols across 14 files)
FILE: logger.go
type LoggerEntry (line 13) | type LoggerEntry struct
type ALogger (line 30) | type ALogger interface
type Logger (line 36) | type Logger struct
method SetFormat (line 50) | func (l *Logger) SetFormat(format string) {
method SetDateFormat (line 54) | func (l *Logger) SetDateFormat(format string) {
method ServeHTTP (line 58) | func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, ne...
function NewLogger (line 44) | func NewLogger() *Logger {
FILE: logger_test.go
function Test_Logger (line 12) | func Test_Logger(t *testing.T) {
function Test_LoggerURLEncodedString (line 36) | func Test_LoggerURLEncodedString(t *testing.T) {
function Test_LoggerCustomFormat (line 63) | func Test_LoggerCustomFormat(t *testing.T) {
FILE: negroni.go
constant DefaultAddress (line 11) | DefaultAddress = ":8080"
function init (line 14) | func init() {
type Handler (line 24) | type Handler interface
type HandlerFunc (line 30) | type HandlerFunc
method ServeHTTP (line 32) | func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request...
type middleware (line 36) | type middleware struct
method ServeHTTP (line 50) | func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
function newMiddleware (line 43) | func newMiddleware(handler Handler, next *middleware) middleware {
function Wrap (line 57) | func Wrap(handler http.Handler) Handler {
function WrapFunc (line 67) | func WrapFunc(handlerFunc http.HandlerFunc) Handler {
type Negroni (line 77) | type Negroni struct
method With (line 92) | func (n *Negroni) With(handlers ...Handler) *Negroni {
method ServeHTTP (line 110) | func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
method Use (line 115) | func (n *Negroni) Use(handler Handler) {
method UseFunc (line 125) | func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *...
method UseHandler (line 130) | func (n *Negroni) UseHandler(handler http.Handler) {
method UseHandlerFunc (line 135) | func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWrit...
method Run (line 143) | func (n *Negroni) Run(addr ...string) {
method Handlers (line 161) | func (n *Negroni) Handlers() []Handler {
function New (line 83) | func New(handlers ...Handler) *Negroni {
function Classic (line 106) | func Classic() *Negroni {
function detectAddress (line 150) | func detectAddress(addr ...string) string {
function build (line 165) | func build(handlers []Handler) middleware {
function voidMiddleware (line 180) | func voidMiddleware() middleware {
FILE: negroni_bench_test.go
type voidHandler (line 8) | type voidHandler struct
method ServeHTTP (line 10) | func (vh *voidHandler) ServeHTTP(rw http.ResponseWriter, r *http.Reque...
function BenchmarkNegroni (line 14) | func BenchmarkNegroni(b *testing.B) {
FILE: negroni_test.go
function expect (line 12) | func expect(t *testing.T, a interface{}, b interface{}) {
function refute (line 18) | func refute(t *testing.T, a interface{}, b interface{}) {
function TestNegroniRun (line 24) | func TestNegroniRun(t *testing.T) {
function TestNegroniWith (line 29) | func TestNegroniWith(t *testing.T) {
function TestNegroniWith_doNotModifyOriginal (line 62) | func TestNegroniWith_doNotModifyOriginal(t *testing.T) {
function TestNegroniServeHTTP (line 102) | func TestNegroniServeHTTP(t *testing.T) {
function TestHandlers (line 130) | func TestHandlers(t *testing.T) {
function TestNegroni_Use_Nil (line 151) | func TestNegroni_Use_Nil(t *testing.T) {
function TestDetectAddress (line 163) | func TestDetectAddress(t *testing.T) {
function voidHTTPHandlerFunc (line 178) | func voidHTTPHandlerFunc(rw http.ResponseWriter, r *http.Request) {
function TestWrap (line 183) | func TestWrap(t *testing.T) {
function TestWrapFunc (line 196) | func TestWrapFunc(t *testing.T) {
FILE: recovery.go
constant NoPrintStackBodyString (line 15) | NoPrintStackBodyString = "500 Internal Server Error"
constant panicText (line 17) | panicText = "PANIC: %s\n%s"
constant panicHTML (line 18) | panicHTML = `<html>
constant nilRequestMessage (line 65) | nilRequestMessage = "Request is nil"
type PanicInformation (line 72) | type PanicInformation struct
method StackAsString (line 79) | func (p *PanicInformation) StackAsString() string {
method RequestDescription (line 84) | func (p *PanicInformation) RequestDescription() string {
type PanicFormatter (line 99) | type PanicFormatter interface
type TextPanicFormatter (line 110) | type TextPanicFormatter struct
method FormatPanicError (line 112) | func (t *TextPanicFormatter) FormatPanicError(rw http.ResponseWriter, ...
type HTMLPanicFormatter (line 122) | type HTMLPanicFormatter struct
method FormatPanicError (line 124) | func (t *HTMLPanicFormatter) FormatPanicError(rw http.ResponseWriter, ...
type Recovery (line 132) | type Recovery struct
method ServeHTTP (line 158) | func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request...
function NewRecovery (line 147) | func NewRecovery() *Recovery {
FILE: recovery_test.go
function TestRecovery (line 13) | func TestRecovery(t *testing.T) {
function TestRecovery_noContentTypeOverwrite (line 43) | func TestRecovery_noContentTypeOverwrite(t *testing.T) {
function TestRecovery_callbackPanic (line 59) | func TestRecovery_callbackPanic(t *testing.T) {
function TestRecovery_handlerPanic (line 79) | func TestRecovery_handlerPanic(t *testing.T) {
type testOutput (line 99) | type testOutput struct
method FormatPanicError (line 108) | func (t *testOutput) FormatPanicError(rw http.ResponseWriter, r *http....
function newTestOutput (line 103) | func newTestOutput() *testOutput {
function formatInfos (line 112) | func formatInfos(infos *PanicInformation) string {
function TestRecovery_formatter (line 115) | func TestRecovery_formatter(t *testing.T) {
function TestRecovery_PanicInformation (line 136) | func TestRecovery_PanicInformation(t *testing.T) {
function TestRecovery_HTMLFormatter (line 161) | func TestRecovery_HTMLFormatter(t *testing.T) {
FILE: response_writer.go
type ResponseWriter (line 11) | type ResponseWriter interface
type beforeFunc (line 26) | type beforeFunc
function NewResponseWriter (line 29) | func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
type responseWriter (line 37) | type responseWriter struct
method WriteHeader (line 46) | func (rw *responseWriter) WriteHeader(s int) {
method Write (line 64) | func (rw *responseWriter) Write(b []byte) (int, error) {
method ReadFrom (line 77) | func (rw *responseWriter) ReadFrom(r io.Reader) (n int64, err error) {
method Unwrap (line 88) | func (rw *responseWriter) Unwrap() http.ResponseWriter {
method Status (line 92) | func (rw *responseWriter) Status() int {
method Size (line 100) | func (rw *responseWriter) Size() int {
method Written (line 104) | func (rw *responseWriter) Written() bool {
method Before (line 108) | func (rw *responseWriter) Before(before func(ResponseWriter)) {
method callBefore (line 112) | func (rw *responseWriter) callBefore() {
FILE: response_writer_feature.go
constant flusher (line 10) | flusher = 1 << iota
constant hijacker (line 11) | hijacker
constant closeNotifier (line 12) | closeNotifier
type flusherFeature (line 16) | type flusherFeature struct
method Flush (line 21) | func (f flusherFeature) Flush() {
type hijackerFeature (line 17) | type hijackerFeature struct
method Hijack (line 29) | func (f hijackerFeature) Hijack() (net.Conn, *bufio.ReadWriter, error) {
type closeNotifierFeature (line 18) | type closeNotifierFeature struct
method CloseNotify (line 33) | func (f closeNotifierFeature) CloseNotify() <-chan bool {
function initFeaturePicker (line 39) | func initFeaturePicker() {
function wrapFeature (line 92) | func wrapFeature(w *responseWriter) ResponseWriter {
FILE: response_writer_pusher.go
method Push (line 10) | func (rw *responseWriter) Push(target string, opts *http.PushOptions) er...
FILE: response_writer_pusher_test.go
type pusherRecorder (line 11) | type pusherRecorder struct
method Push (line 20) | func (c *pusherRecorder) Push(target string, opts *http.PushOptions) e...
function newPusherRecorder (line 16) | func newPusherRecorder() *pusherRecorder {
function TestResponseWriterPush (line 25) | func TestResponseWriterPush(t *testing.T) {
FILE: response_writer_test.go
type closeNotifyingRecorder (line 13) | type closeNotifyingRecorder struct
method close (line 25) | func (c *closeNotifyingRecorder) close() {
method CloseNotify (line 29) | func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
function newCloseNotifyingRecorder (line 18) | func newCloseNotifyingRecorder() *closeNotifyingRecorder {
type hijackableResponse (line 33) | type hijackableResponse struct
method Header (line 41) | func (h *hijackableResponse) Header() http.Header { return n...
method Write (line 42) | func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0...
method WriteHeader (line 43) | func (h *hijackableResponse) WriteHeader(code int) {}
method Flush (line 44) | func (h *hijackableResponse) Flush() {}
method Hijack (line 45) | func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, er...
function newHijackableResponse (line 37) | func newHijackableResponse() *hijackableResponse {
function TestResponseWriterBeforeWrite (line 50) | func TestResponseWriterBeforeWrite(t *testing.T) {
function TestResponseWriterBeforeFuncHasAccessToStatus (line 58) | func TestResponseWriterBeforeFuncHasAccessToStatus(t *testing.T) {
function TestResponseWriterBeforeFuncCanChangeStatus (line 72) | func TestResponseWriterBeforeFuncCanChangeStatus(t *testing.T) {
function TestResponseWriterBeforeFuncChangesStatusMultipleTimes (line 85) | func TestResponseWriterBeforeFuncChangesStatusMultipleTimes(t *testing.T) {
function TestResponseWriterWritingString (line 100) | func TestResponseWriterWritingString(t *testing.T) {
function TestResponseWriterWrittenStatusCode (line 113) | func TestResponseWriterWrittenStatusCode(t *testing.T) {
function TestResponseWriterWritingStrings (line 136) | func TestResponseWriterWritingStrings(t *testing.T) {
function TestResponseWriterWritingHeader (line 149) | func TestResponseWriterWritingHeader(t *testing.T) {
function TestResponseWriterWritingHeaderTwice (line 161) | func TestResponseWriterWritingHeaderTwice(t *testing.T) {
function TestResponseWriterBefore (line 174) | func TestResponseWriterBefore(t *testing.T) {
function TestResponseWriterUnwrap (line 195) | func TestResponseWriterUnwrap(t *testing.T) {
function TestResponseWriterHijack (line 206) | func TestResponseWriterHijack(t *testing.T) {
function TestResponseWriteHijackNotOK (line 218) | func TestResponseWriteHijackNotOK(t *testing.T) {
function TestResponseWriterCloseNotify (line 225) | func TestResponseWriterCloseNotify(t *testing.T) {
function TestResponseWriterNonCloseNotify (line 239) | func TestResponseWriterNonCloseNotify(t *testing.T) {
function TestResponseWriterFlusher (line 245) | func TestResponseWriterFlusher(t *testing.T) {
function TestResponseWriter_Flush_marksWritten (line 253) | func TestResponseWriter_Flush_marksWritten(t *testing.T) {
type mockReader (line 263) | type mockReader struct
method Read (line 268) | func (r *mockReader) Read(p []byte) (n int, err error) {
function TestResponseWriterWithoutReadFrom (line 277) | func TestResponseWriterWithoutReadFrom(t *testing.T) {
type mockResponseWriterWithReadFrom (line 292) | type mockResponseWriterWithReadFrom struct
method ReadFrom (line 297) | func (rw *mockResponseWriterWithReadFrom) ReadFrom(r io.Reader) (n int...
function TestResponseWriterWithReadFrom (line 307) | func TestResponseWriterWithReadFrom(t *testing.T) {
FILE: static.go
type Static (line 14) | type Static struct
method ServeHTTP (line 32) | func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, ne...
function NewStatic (line 24) | func NewStatic(directory http.FileSystem) *Static {
FILE: static_test.go
function TestStatic (line 10) | func TestStatic(t *testing.T) {
function TestStaticHead (line 29) | func TestStaticHead(t *testing.T) {
function TestStaticAsPost (line 49) | func TestStaticAsPost(t *testing.T) {
function TestStaticBadDir (line 65) | func TestStaticBadDir(t *testing.T) {
function TestStaticOptionsServeIndex (line 80) | func TestStaticOptionsServeIndex(t *testing.T) {
function TestStaticOptionsPrefix (line 97) | func TestStaticOptionsPrefix(t *testing.T) {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (190K chars).
[
{
"path": ".gitignore",
"chars": 14,
"preview": "/coverage.txt\n"
},
{
"path": ".travis.yml",
"chars": 939,
"preview": "language: go\n\nsudo: false\ndist: trusty\narch:\n - AMD64\n - ppc64le\n \ngo:\n- 1.x\n- 1.2.x\n- 1.3.x\n- 1.4.x\n- 1.5.x\n- 1.6.x\n"
},
{
"path": "CHANGELOG.md",
"chars": 4888,
"preview": "# Change Log\n\n**ATTN**: This project uses [semantic versioning](http://semver.org/).\n\n## [3.1.1] - [2024-06-04]\n\n### Fix"
},
{
"path": "LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2025 Jeremy Saenz\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 21500,
"preview": "# Negroni\n[](http://godoc.org/github.com/urfave/negro"
},
{
"path": "doc.go",
"chars": 702,
"preview": "// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of ne"
},
{
"path": "go.mod",
"chars": 45,
"preview": "module github.com/urfave/negroni/v3\n\ngo 1.17\n"
},
{
"path": "logger.go",
"chars": 1917,
"preview": "package negroni\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"text/template\"\n\t\"time\"\n)\n\n// LoggerEntry is the structure "
},
{
"path": "logger_test.go",
"chars": 2101,
"preview": "package negroni\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc Test_Logger(t *"
},
{
"path": "negroni.go",
"chars": 5674,
"preview": "package negroni\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nconst (\n\t// DefaultAddress is used if no other is specified.\n\tDefa"
},
{
"path": "negroni_bench_test.go",
"chars": 597,
"preview": "package negroni\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n)\n\ntype voidHandler struct{}\n\nfunc (vh *voidHandler) ServeHTTP(rw http."
},
{
"path": "negroni_test.go",
"chars": 5395,
"preview": "package negroni\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n)\n\n/* Test Helpers */\nfunc expect"
},
{
"path": "recovery.go",
"chars": 5778,
"preview": "package negroni\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"text/template\"\n)\n\nconst (\n\t// No"
},
{
"path": "recovery_test.go",
"chars": 5010,
"preview": "package negroni\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestReco"
},
{
"path": "response_writer.go",
"chars": 3318,
"preview": "package negroni\n\nimport (\n\t\"io\"\n\t\"net/http\"\n)\n\n// ResponseWriter is a wrapper around http.ResponseWriter that provides e"
},
{
"path": "response_writer_feature.go",
"chars": 2555,
"preview": "package negroni\n\nimport (\n\t\"bufio\"\n\t\"net\"\n\t\"net/http\"\n)\n\nconst (\n\tflusher = 1 << iota\n\thijacker\n\tcloseNotifier\n)\n\ntype ("
},
{
"path": "response_writer_pusher.go",
"chars": 319,
"preview": "//+build go1.8\n\npackage negroni\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n)\n\nfunc (rw *responseWriter) Push(target string, opts *h"
},
{
"path": "response_writer_pusher_test.go",
"chars": 648,
"preview": "//+build go1.8\n\npackage negroni\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\ntype pusherRecorder struct {\n\t*"
},
{
"path": "response_writer_test.go",
"chars": 7672,
"preview": "package negroni\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype closeNotifyi"
},
{
"path": "static.go",
"chars": 2065,
"preview": "package negroni\n\nimport (\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n)\n\n// Static is a middleware handler that serves static files i"
},
{
"path": "static_test.go",
"chars": 2397,
"preview": "package negroni\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestStatic(t *testing.T) {\n\trespo"
},
{
"path": "translations/README_de_de.md",
"chars": 9884,
"preview": "# Negroni [](http://godoc.org/github.com/urfave/negro"
},
{
"path": "translations/README_fr_FR.md",
"chars": 18796,
"preview": "# Negroni\n[](http://godoc.org/github.com/urfave/negro"
},
{
"path": "translations/README_ja_JP.md",
"chars": 12195,
"preview": "# Negroni [](http://godoc.org/github.com/urfave/negro"
},
{
"path": "translations/README_ko_KR.md",
"chars": 17415,
"preview": "# Negroni\n\n[](http://godoc.org/github.com/urfave/negr"
},
{
"path": "translations/README_pt_br.md",
"chars": 16954,
"preview": "# Negroni\n[](http://godoc.org/github.com/urfave/negro"
},
{
"path": "translations/README_zh_CN.md",
"chars": 17584,
"preview": "# Negroni\n[](http://godoc.org/github.com/urfave/negro"
},
{
"path": "translations/README_zh_tw.md",
"chars": 13025,
"preview": "# Negroni(尼格龍尼)\n[](http://godoc.org/github.com/urfave"
}
]
About this extraction
This page contains the full source code of the urfave/negroni GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (176.2 KB), approximately 52.6k tokens, and a symbol index with 147 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.