Negroni

Golang 的惯用 HTTP 中间件。(Idiomatic HTTP Middleware for Golang)

  • Owner: urfave/negroni
  • Platform: BSD, Linux, Mac, Windows
  • License:: MIT License
  • Category::
  • Topic:
  • Like:
    0
      Compare:

Github stars Tracking Chart

Negroni

注意: 本函数库原本位于github.com/codegangsta/negroni -- Github 会自动将请求重定向到当前地址, 但我们建议你更新一下引用路径。

在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它是一个具备微型、非嵌入式、鼓励使用原生 net/http 库特征的中间件。

如果你喜欢用 Martini ,但又觉得它太魔幻,那么 Negroni 就是你很好的选择了。

各国语言翻译:

入门指导

当安装了 Go 语言并设置好了 GOPATH 后,新建第一个 .go 文件,命名为 server.go

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

然后安装 Negroni 包(注意:要求 Go 1.1 或更高的版本的 Go 语言环境):

go get github.com/urfave/negroni
    

最后运行刚建好的 server.go 文件:

go run server.go
    

这时一个 Go net/http Web 服务器会跑在 localhost:3000 上,使用浏览器打开 localhost:3000 可看到输出的结果。

第三方包

如果你使用 Debian 系统,你可以执行 apt install golang-github-urfave-negroni-dev 来安装 negroni包地址 (写该文档时,它是在 sid 仓库中).

Negroni 是一个框架吗?

Negroni 是一个框架,它是为了方便使用 net/http 而设计的一个库而已。

路由呢?

Negroni 没有带路由功能,使用 Negroni 时,需要找一个适合你的路由。不过好在 Go 社区里已经有相当多可用的路由,Negroni 更喜欢和那些完全支持 net/http 库的路由搭配使用,比如搭配 Gorilla Mux 路由器,这样使用:

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 -- 请求 /响应 log 日志中间件
  • negroni.Static -- 静态文件处理中间件,默认目录在 "public" 下.

这使得从 Negroni 开始使用一些有用的特性变得非常容易。

Handlers (处理器)

Negroni 提供双向的中间件机制,这个特征很棒,都是得益于 negroni.Handler 这个接口。

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

在中间件没有写入 ResponseWriter 响应前,它会在中间件链上调用 next http.HandlerFunc 先执行, 以下代码就是优雅的使用方式:

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

你也可以用 Use 函数把这些 http.Handler 处理器引入到处理器链上来:

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

你也可以使用 UseHandlerhttp.Handlers 处理器引入。

n := negroni.New()

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

n.UseHandler(mux)

http.ListenAndServe(":3000", n)
                        

With()

Negroni 还有一个便利的函数叫 With. With 函数可以把一个或多个 Handler 实例和接收者处理器集合组合成新的处理器集合,并返回新的 Negroni 实例对象。

// 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.

package main

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

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

未提供地址的情况下,会使用 PORT 系统环境变量, 若未定义该系统环境变量则会用预设的地址, 请参考 Run 详情说明。

一般来说使用 net/http 方法, 并且将 Negroni 当作处理器传入, 这样可定制化更佳, 例如:

package main

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

  "github.com/urfave/negroni"
)

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

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

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

特定路由(分组路由)

如果你需要一组路由功能,需要借助特定的路由中间件完成,做法很简单,只需建立一个新 Negroni 实例,传入路由处理器里即可。

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, 下面是一个使用 subrouter 的例子:

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

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

With() 可被用来减少在跨路由分享时多余的中间件的冗余.

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),
))                                            

内置中间件

静态文件处理

中间件通过文件系统 filesystem 来代理(处理)静态文件。 一旦文件不存在, 请求代理会转到下个中间件。如果你想要处理不存在的文件返回 404 File Not Found HTTP 错误, 请参考 http.FileServer 处理器吧.

例子:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

中间件首先从 /tmp 找文件,一旦在文件系统中找不到匹配的文件,代理将调用下一个文件。

恢复

本中间件接收 panic 跟错误代码 500 的响应。如果其他中间件写了响应代码或 Body 内容的话, 中间件会无法顺利地传送 500 错误给客户端, 因为客户端已经收到 HTTP 响应。另外, 可以 像 Sentry 或 Airbrake 挂载 PanicHandlerFunc 来报 500 错误给系统。

例子:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

它将输出 500 Internal Server Error 给每个请求. 如果 PrintStack 设成 true (默认值)的话,它也会把错误日志写入请求方追踪堆栈。

加错误处理器的例子:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

默认情况下,这个中间件会简要输出日志信息到 STDOUT 上。当然你也可以通过 SetFormatter() 函数自定义输出的日志。

当发生崩溃时,同样你也可以通过 HTMLPanicFormatter 来显示美化的 HTML 输出结果。

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

Logger

该中间件负责打印各个请求和响应日志.

例子:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

每个请求打印日志将如下:

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

当然你可以调用 SetFormat 来自定义日志的格式。格式是一个预定义了字段的 LoggerEntry 结构体。正如以下代码: -

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

第三方兼容中间件

以下是兼容 Negroni 的中间件列表,如果你也有兼容 Negroni 的中间件,如果想提交自己的中间件,建议你附上 PR 链接。

中间件 作者 描述
authz Yang Luo 支持ACL, RBAC, ABAC的权限管理中间件,基于Casbin
binding Matt Holt HTTP 请求数据注入到 structs 实体
cloudwatch Colin Steele AWS CloudWatch 矩阵的中间件
cors Olivier Poitrey Cross Origin Resource Sharing (CORS) support
csp Awake Networks 基于Content Security Policy(CSP)
delay Jeff Martinez 为endpoints增加延迟时间. 在测试严重网路延迟的效应时好用
New Relic Go Agent Yadvendar Champawat 官网 New Relic Go Agent (目前正在测试阶段)
gorelic Jingwen Owen Ou New Relic agent for Go runtime
Graceful Tyler Bunnell 优雅关闭 HTTP 的中间件
gzip phyber 响应流 GZIP 压缩
JWT Middleware Auth0 Middleware checks for a JWT on the Authorization header on incoming requests and decodes it
logrus Dan Buch 基于 Logrus-based logger 日志
oauth2 David Bochenski oAuth2 中间件
onthefly Alexander Rødseth 快速生成 TinySVG, HTML and CSS 中间件
permissions2 Alexander Rødseth Cookies, 用户和权限
prometheus Rene Zbinden 简易建立矩阵端点给prometheus建构工具
render Cory Jacobsen 渲染 JSON, XML and HTML 中间件
RestGate Prasanga Siripala REST API 接口的安全认证
secure Cory Jacobsen Middleware that implements a few quick security wins
sessions David Bochenski Session 会话管理
stats Florent Messa 检测 web 应用当前运行状态信息 (响应时间等等。)
VanGoH Taylor Wrobel Configurable AWS-Style 基于 HMAC 鉴权认证的中间件
xrequestid Andrea Franz 给每个请求指定一个随机 X-Request-Id 头的中间件
mgo session Joel James 处理在每个请求建立与关闭 mgo sessions
digits Bilal Amarni 处理 Twitter Digits 的认证
stats Chirag Gupta endpoints用的管理QPS与延迟状态的中间件非同步地将状态刷入InfluxDB
Chaos Marc Falzon 以编程方式在应用程式中插入无序行为的中间件

范例

Alexander Rødseth 创建的 mooseware 是一个编写兼容 Negroni 中间件的处理器骨架的范例。

即时编译

ginfresh 这两个应用是即时编译的 Negroni 工具,推荐用户开发的时候使用。

Go & Negroni 初学者必读推荐

关于 Negroni

Negroni 原由 Code Gangsta 主导设计开发。

Main metrics

Overview
Name With Ownerurfave/negroni
Primary LanguageGo
Program languageGo (Language Count: 1)
PlatformBSD, Linux, Mac, Windows
License:MIT License
所有者活动
Created At2014-05-18 22:09:10
Pushed At2025-05-03 19:46:24
Last Commit At2025-05-03 12:46:24
Release Count10
Last Release Namev3.1.1 (Posted on 2024-06-04 15:23:08)
First Release Namev0.1.0 (Posted on )
用户参与
Stargazers Count7.5k
Watchers Count223
Fork Count580
Commits Count442
Has Issues Enabled
Issues Count103
Issue Open Count7
Pull Requests Count147
Pull Requests Open Count2
Pull Requests Close Count28
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private

Negroni

GoDoc
Build Status
codebeat
codecov

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

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

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

Language Translations:

Getting Started

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

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

go get github.com/urfave/negroni

Then run your server:

go run server.go

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

Packaging

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

Is Negroni a Framework?

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

Routing?

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

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

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

http.ListenAndServe(":3001", n)

negroni.Classic()

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

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

Handlers

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

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

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

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

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

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

You can also map plain old http.Handlers:

n := negroni.New()

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

n.UseHandler(mux)

http.ListenAndServe(":3000", n)

With()

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

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

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

Run()

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

package main

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

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

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

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

package main

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

  "github.com/urfave/negroni"
)

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

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

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

Route Specific Middleware

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

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

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

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

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

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

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

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

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

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

Bundled Middleware

Static

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

Example:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

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

Recovery

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

Example:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

Example with error handler:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

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

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

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

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

Logger

This middleware logs each incoming request and response.

Example:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

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

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

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

Will print a log similar to:

[negroni] 2017-10-04T14:56:25+02:00