Negroniと他のGoミドルウェアソリューション: 何がユニークなのか?
Ethan Miller
Product Engineer · Leapcell

Negroniライブラリ入門
NegroniはHTTPミドルウェアに焦点を当てたライブラリです。サイズが小さく、非侵入型であり、標準ライブラリのnet/http
からのハンドラーの使用を推奨しています。この記事では、このライブラリについて詳しく紹介します。
I. ミドルウェアを使用する理由
開発プロセス中には、統計、ログ、デバッグなど、いくつかの共通のロジックコードがあり、これらのロジックは各ハンドラーで必要となる場合があります。これらのコードを各ハンドラーに1つずつ追加すると、面倒になるだけでなく、エラーや省略が発生しやすくなります。
ハンドラーの時間消費をカウントする例を考えてみましょう。時間消費をカウントするコードを各ハンドラーに追加する場合、例は次のようになります。
package main import ( "fmt" "net/http" "time" ) func index(w http.ResponseWriter, r *http.Request) { start := time.Now() fmt.Fprintf(w, "leapcell page") fmt.Printf("index elasped:%fs", time.Since(start).Seconds()) } func greeting(w http.ResponseWriter, r *http.Request) { start := time.Now() name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) fmt.Printf("greeting elasped:%fs\n", time.Since(start).Seconds()) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.HandleFunc("/greeting", greeting) http.ListenAndServe(":8000", mux) }
ただし、このアプローチには多くの欠点があります。
- 柔軟性の欠如: 新しいハンドラーが追加されるたびに、時間消費をカウントするためのこの部分のコードを追加する必要があり、これらのコードは実際のハンドラーロジックと直接的な関係がありません。
- 省略しやすい: ハンドラーを作成するときに、このようなコードを追加するのを忘れやすく、特に考えられるすべての戻りパスを考慮すると、コーディングの負担が増加します。
- 修正に不向き: 統計コードにエラーがある場合、または調整が必要な場合、関係するすべてのハンドラーを修正する必要があります。
- 新しいロジックの追加が困難: 他の統計ロジックを追加する必要がある場合、すべてのハンドラーのコードも変更する必要があります。
Go言語のクロージャ機能を利用することで、実際のハンドラーコードを関数にカプセル化し、この関数で追加のロジックを実行できます。以下に示すとおりです。
func elasped(h func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path start := time.Now() h(w, r) fmt.Printf("path:%s elasped:%fs\n", path, time.Since(start).Seconds()) } } func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "leapcell page") } func greeting(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", elasped(index)) mux.HandleFunc("/greeting", elasped(greeting)) http.ListenAndServe(":8000", mux) }
この例では、ハンドラーとは関係のない追加のコードがelasped
関数に配置されています。ハンドラー関数を登録するとき、元のハンドラー関数を直接使用する代わりに、elasped
関数でカプセル化されています。実際、elasped
のような関数はミドルウェアです。元のハンドラー関数をカプセル化し、新しいハンドラー関数を返し、実際の処理ロジックの前後にコードを挿入するのに便利で、追加、変更、および保守に役立ちます。
II. Negroniのクイックスタート
(I) インストール
次のコマンドを実行して、Negroniライブラリをインストールします。
$ go get github.com/urfave/negroni
(II) 使用例
package main import ( "fmt" "net/http" "github.com/urfave/negroni" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Negroniは比較的使いやすく、http.Handler
と簡単に連携できます。negroni.Classic()
は、いくつかの一般的に使用されるミドルウェアを提供します。
- negroni.Recovery: プログラムの実行中に発生する
panic
から回復するために使用されます。ハンドラーコードでpanic
が発生した場合、このミドルウェアは例外をキャッチし、プログラムが終了するのを防ぎます。 - negroni.Logger: リクエストとレスポンスに関する基本情報を記録し、ロギング機能を実装します。
- negroni.Static:
public
ディレクトリ内の静的ファイルを提供できます。
n.UseHandler(mux)
を呼び出すことで、これらのミドルウェアがマルチプレクサmux
に適用されます。プログラムを実行した後、ブラウザでlocalhost:3000
にアクセスすると、コンソールに次のような出力が表示されます。
[negroni] 2025-03-22T18:48:53+08:00 | 200 | 10.9966ms | localhost:8080 | GET /
III. Negroniの詳細な機能
(I) negroni.Handlerインターフェース
negroni.Handler
インターフェースは、ミドルウェアの実行プロセスをより柔軟に制御できます。インターフェースは次のように定義されています。
type Handler interface { ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) }
記述されたミドルウェアのシグネチャは、func(http.ResponseWriter, *http.Request, http.HandlerFunc)
であるか、negroni.Handler
インターフェースを実装する必要があります。例:
func RandomMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if rand.Int31n(100) <= 50 { fmt.Fprintf(w, "hello from RandomMiddleware") } else { next(w, r) } } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.Use(negroni.HandlerFunc(RandomMiddleware)) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
上記のコードは、ランダムなミドルウェアを実装しています。レスポンスがRandomMiddleware
ミドルウェアから直接返される確率は50%であり、実際のハンドラー関数が実行される確率は50%です。プログラムを実行した後、ブラウザでlocalhost:8080
を継続的に更新して、効果を観察します。
実際、func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
は便利な記述方法です。n.Use
を呼び出すと、negroni.HandlerFunc
でカプセル化され、negroni.HandlerFunc
はnegroni.Handler
インターフェースを実装します。以下は関連するコードです。
// src/github.com/urfave/negroni/negroni.go 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) }
同様に、net/http
では、func(http.ResponseWriter, *http.Request)
もhttp.HandlerFunc
でカプセル化され、http.Handler
インターフェースを実装します。
(II) negroni.Withメソッド
複数のミドルウェアがある場合、n.Use()
メソッドを使用して1つずつ追加するのは面倒です。NegroniはWith()
メソッドを提供し、1つまたは複数のnegroni.Handler
パラメータを受け取り、新しいオブジェクトを返します。例:
func Middleware1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware A begin") next(w, r) fmt.Println("Middleware A end") } func Middleware2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware B begin") next(w, r) fmt.Println("Middleware B end") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n = n.With( negroni.HandlerFunc(Middleware1), negroni.HandlerFunc(Middleware2), ) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
(III) Runメソッド
Negroniオブジェクトは、サーバープログラムを簡単に実行するために使用されるRun()
メソッドを提供します。このメソッドは、http.ListenAndServe()
と同じアドレス(Addr)パラメータを受け入れます。例:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.UseHandler(mux) n.Run(":8080") }
ポートが指定されていない場合、Run()
メソッドはPORT
環境変数を使用しようとします。PORT
環境変数も設定されていない場合、デフォルトのポート:8080
が使用されます。
(IV) http.Handlerとして使用する
Negroniはnet/http
プログラムで簡単に使用でき、negroni.Negroni
オブジェクトをhttp.Handler
として対応するメソッドに直接渡すことができます。例:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) s := &http.Server{ Addr: ":8080", Handler: n, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } s.ListenAndServe() }
IV. 組み込みミドルウェア
(I) Static
negroni.Static
ミドルウェアは、指定されたディレクトリ内のファイルを提供できます。サンプルコードは次のとおりです。
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() n.Use(negroni.NewStatic(http.Dir("./public"))) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
プログラムの実行ディレクトリにpublic
ディレクトリを作成し、その中にいくつかのファイル(1.txt
、2.jpg
など)を配置します。プログラムの実行後、ブラウザでlocalhost:8080/1.txt
とlocalhost:8080/2.jpg
にアクセスして、これらのファイルをリクエストできます。対応するファイルが見つからない場合、Static
ミドルウェアはリクエストを次のミドルウェアまたはハンドラー関数に渡すことに特に注意してください。たとえば、ブラウザにlocalhost:3000/none - exist.txt
と入力すると、hello world
のレスポンスが表示されます。
(II) Logger
「クイックスタート」セクションでは、このミドルウェアはnegroni.Classic()
を通じて使用されています。単独で使用してリクエスト情報を記録することもでき、SetFormat()
メソッドを呼び出してログ形式を設定できます。例:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() logger := negroni.NewLogger() logger.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}") n.Use(logger) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
上記のコードは、ログ形式を[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}
に設定しています。つまり、レスポンスステータス、時間消費、およびUserAgent
を記録します。
V. サードパーティミドルウェア
組み込みのミドルウェアに加えて、Negroniには多くのサードパーティミドルウェアもあります。完全なリストについては、https://github.com/urfave/negroni?tab=readme-ov-file#third-party-middlewareを参照してください。
VI. 結論
Negroniはミドルウェアの機能に焦点を当てており、面倒でめったに使用されない派手な機能はあまりありません。その非侵入型の設計機能により、標準ライブラリnet/http
や他のWebライブラリ(gorilla/mux
など)とシームレスに連携できます。この機能は、HTTPサービスの構築時に開発者に大きな利便性を提供します。開発者は、既存のコードアーキテクチャを大規模に変更することなく、実際のニーズに応じてミドルウェアを柔軟に選択および組み合わせて、ログ、エラー処理、リクエスト統計などのさまざまな機能を実現できます。
全体として、NegroniはGo言語のHTTPサービス開発において非常に価値のあるライブラリです。効率的で柔軟性があり、保守が容易なWebアプリケーション開発を追求する人にとって、Negroniは間違いなく深く理解し、広く適用する価値のある優れたツールです。
Leapcell: 最高のサーバーレスWebホスティング
最後に、Goサービスをデプロイするための最適なプラットフォームをご紹介します。Leapcell
🚀 お気に入りの言語で構築
JavaScript、Python、Go、またはRustで簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイ
使用した分だけを支払います—リクエストも料金もありません。
⚡ 従量課金制、隠れたコストなし
アイドル料金はなく、シームレスなスケーラビリティだけです。
🔹 Twitterでフォローしてください: @LeapcellHQ