NegroniはGoによる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 パッケージをインストールします (NOTE: >= go 1.1 以上のバージョンが必要です):
go get github.com/urfave/negroni
インストールが完了したら、サーバーを起動しましょう。
go run server.go
すると、Go標準パッケージの net/http
によるWebサーバーがlocalhost:3000
で起動します。
Negroni はrevel やmartini のようなフレームワークではありません。 Negroniは net/http
と直接結びついて動作する、ミドルウェアにフォーカスされたライブラリです。
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.Recovery
- Panic Recovery Middleware.negroni.Logger
- Request/Response Logger Middleware.negroni.Static
- "public"ディレクトリの静的ファイルの処理
これらはNegroni の便利な機能を利用し始めるのをとても簡単にしてくれます。
Negroniは双方向のミドルウェアのフローを提供します。これは、negroni.Handler
インターフェースを通じて行われます。
type Handler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
ミドルウェアが既にResponseWriter
に書き込み処理を行っていない場合、次のミドルウェア・ハンドラを動かすために、チェーン内の次のhttp.HandlerFunc
を呼び出す必要があります。
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// 前処理
next(rw, r)
// 後処理
}
この時、MyMiddleware
をUse
関数によってハンドラチェーンに割り当てることができます。
n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))
また、標準パッケージに備わっているhttp.Handler
をハンドラチェーンに割り当てることもできます。
n := negroni.New()
mux := http.NewServeMux()
// ルーティングの処理
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
Run
はアドレスの文字列を受け取り、http.ListenAndServe
と同様にサーバーを起動します。
package main
import (
"github.com/urfave/negroni"
)
func main() {
n := negroni.Classic()
n.Run(":8080")
}
通常、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()
// admin 関連のルーティングをココに記述
// admin のミドルウェアとして、Negroni インスタンスを作成
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
もしGorilla Muxを利用する場合、サブルーターを使うサンプルは以下の通りです。
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),
))
このミドルウェアは、ファイルシステム上のファイルをサーバーからクライアントに送信します。もし指定されたファイルが存在しない場合、次のミドルウェアにリクエストの処理を依頼します。存在しないファイルへのアクセスに対して404 Not Found
を返したい場合、 http.FileServer をハンドラとして利用すべきです。
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)
}
まず、/tmp
ディレクトリからファイルをクライアントに送ろうとしますが、指定されたファイルがファイルシステム上に存在しない場合、プロキシは次のハンドラを呼び出します。
このミドルウェアは、panic
を受け取ると、500 internal Server Error
をレスポンスします。
もし他のミドルウェアが既に応答処理を行い、クライアントにHTTP レスポンスが帰っている場合、このミドルウェアは失敗します。
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)
}
上記のコードは、 500 Internal Server Error
を各リクエストごとに返します。
また、スタックトレースをログに出力するだけでなく、PrintStack
がtrue
に設定されている場合、クライアントにスタックトレースを出力します。(デフォルトでtrue
に設定されています。)
このミドルウェアは、送られてきた全てのリクエストとレスポンスを記録します。
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)
}
各リクエストごとに、以下のようなログが出力されます。
[negroni] 2017-10-04T14:56:25+02:00 | 200 | 378µs | localhost:3004 | GET /
Negroni と互換性のあるミドルウェアの一覧です。あなたが作ったミドルウェアをここに追加してもらっても構いません(PRを送って下さい)
注意: ここの一覧は古くなっている可能性があります。英語版のREADME.md を適宜参照して下さい。
ミドルウェア名 | 作者 | 概要 |
---|---|---|
authz | Yang Luo | ACL, RBAC, ABAC Authorization middlware based on Casbin |
binding | Matt Holt | Data binding from HTTP requests into structs |
cloudwatch | Colin Steele | AWS cloudwatch metrics middleware |
cors | Olivier Poitrey | Cross Origin Resource Sharing (CORS) support |
csp | Awake Networks | Content Security Policy (CSP) support |
delay | Jeff Martinez | Add delays/latency to endpoints. Useful when testing effects of high latency |
New Relic Go Agent | Yadvendar Champawat | Official New Relic Go Agent (currently in beta) |
gorelic | Jingwen Owen Ou | New Relic agent for Go runtime |
Graceful | Tyler Bunnell | Graceful HTTP Shutdown |
gzip | phyber | GZIP response compression |
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 middleware |
onthefly | Alexander Rødseth | Generate TinySVG, HTML and CSS on the fly |
permissions2 | Alexander Rødseth | Cookies, users and permissions |
prometheus | Rene Zbinden | Easily create metrics endpoint for the prometheus instrumentation tool |
render | Cory Jacobsen | Render JSON, XML and HTML templates |
RestGate | Prasanga Siripala | Secure authentication for REST API endpoints |
secure | Cory Jacobsen | Middleware that implements a few quick security wins |
sessions | David Bochenski | Session Management |
stats | Florent Messa | Store information about your web application (response time, etc.) |
VanGoH | Taylor Wrobel | Configurable AWS-Style HMAC authentication middleware |
xrequestid | Andrea Franz | Middleware that assigns a random X-Request-Id header to each request |
mgo session | Joel James | Middleware that handles creating and closing mgo sessions per request |
digits | Bilal Amarni | Middleware that handles Twitter Digits authentication |
stats | Chirag Gupta | Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db |
Alexander Rødseth created mooseware, a skeleton for writing a Negroni middleware handler.
gin and fresh both live reload negroni apps.
Negroni は他ならぬCode Gangstaによって異常なまでにデザインされた素晴らしいライブラリです。