Skip to content

Latest commit

 

History

History
490 lines (363 loc) · 16.7 KB

README_zh_CN.md

File metadata and controls

490 lines (363 loc) · 16.7 KB

Negroni

GoDoc Build Status codebeat codecov

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

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

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

各国语言翻译:

入门指导

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

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")
}

然后安装 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)

n.Run(":3000")

negroni.Classic() 经典的实例

negroni.Classic() 提供一些默认的中间件,这些中间件在多数应用都很有用。

  • negroni.Recovery - 异常(恐慌)恢复中间件
  • negroni.Logging - 请求 / 响应 log 日志中间件
  • negroni.Static - 静态文件处理中间件,默认目录在 "public" 下.

negroni.Classic() 它那些通用必要的属性,可以快速上手 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)

n.Run(":3000")

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() // 导入一些预设的中间件
  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.Handle("/admin", 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" 是用来保证subRouter与主要路由连结的必要参数
router.PathPrefix("/subpath").Handler(negroni.New(
  Middleware1,
  Middleware2,
  negroni.Wrap(subRouter),
))

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

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

内置中间件

静态文件处理

中间件通过文件系统 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!")
  })

  // 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 的响应。如果其他中间件写了响应代码或 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) {
    // 在这写些程式回报错误给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 主导设计开发。