Goでのリクエスト処理の要約
GoでのHTTPリクエストの処理は、主にServeMuxesとHandlersの2つです。
ServeMuxは本質的にHTTP要求ルーター(またはマルチプレクサ)です。 着信要求を事前定義されたURLパスのリストと比較し、一致するものが見つかるたびにパスに関連付けられたハンドラーを呼び出します。
ハンドラは、応答ヘッダーと本文を書く責任があります。 ほとんどのオブジェクトは、http.Handler
ServeHTTP
次の署名を持つメソッド:
ServeHTTP(http.ResponseWriter, *http.Request)
GoのHTTPパッケーとRedirectHandler
。 簡単ではあるが工夫された例から始めましょう:
$ mkdir handler-example$ cd handler-example$ touch main.go
package mainimport ( "log" "net/http")func main() { mux := http.NewServeMux() rh := http.RedirectHandler("http://example.org", 307) mux.Handle("/foo", rh) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
これをすばやくステップしてみましょう:
-
main
http.NewServeMux
関数を使用して空のServeMuxを作成します。 - 次に、
http.RedirectHandler
http://example.org
にリダイレクトします。 - 次に
mux.Handle
関数を使用してこれを新しいServeMuxに登録するので、URLパスを持つすべての着信要求のハンドラとして機能します/foo
。 - 最後に、新しいサーバーを作成し、
http.ListenAndServe
関数を使用して着信要求のリッスンを開始し、要求を照合するためのServeMuxを渡します。
先に行ってアプリケーションを実行します。
$ go run main.goListening...
ブラウザでhttp://localhost:3000/foo
あなたの要求が正常にリダイレクトされることがわかります。ListenAndServe関数のシグネチャはListenAndServe(addr string, handler Handler)
ですが、第二のパラメータとしてServeMuxを渡しました。ServeMux型にもServeHTTP
メソッドがあり、ハンドラーインターフェイスも満たすため、これを行うことができました。
私にとっては、ServeMuxを特別な種類のハンドラであると考えるのが単純化されています。 これは最初に聞こえるほど飛躍的なものではありません–ハンドラを一緒に連鎖させることはGoではかなり一般的です。
カスタムハンドラー
指定された形式で現在のローカル時刻で応答するカスタムハンドラーを作成しましょう。
type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}
ここの正確なコードはあまり重要ではありません。
本当に重要なのは、オブジェクト(この場合はtimeHandler
ServeHTTP(http.ResponseWriter, *http.Request)
という署名を持つメソッドを実装していることです。 ハンドラーを作るために必要なのはそれだけです。
これを具体的な例に埋め込みましょう:
package mainimport ( "log" "net/http" "time")type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() th := &timeHandler{format: time.RFC1123} mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
main
timeHandler
timeHandler
&
ポインタを生成するシンボル。 そして、前の例のように、mux.Handle
関数を使用してこれをServeMuxに登録します。アプリケーションを実行すると、ServeMuxは/time
timeHandler.ServeHTTP
メソッドに直接渡します。p>
先に行って試してみてください
: http://localhost:3000/time
timeHandler
を簡単に再利用できることにも注意してください。
func main() { mux := http.NewServeMux() th1123 := &timeHandler{format: time.RFC1123} mux.Handle("/time/rfc1123", th1123) th3339 := &timeHandler{format: time.RFC3339} mux.Handle("/time/rfc3339", th3339) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
func main() { mux := http.NewServeMux() th1123 := &timeHandler{format: time.RFC1123} mux.Handle("/time/rfc1123", th1123) th3339 := &timeHandler{format: time.RFC3339} mux.Handle("/time/rfc3339", th3339) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
ハンドラーとしての関数
新しいカスタム型とservehttpメソッドを定義する単純なケース(上記の例のような)では、少し冗長に感じます。 Goのhttp.HandlerFunc
func(http.ResponseWriter, *http.Request)
シグネチャを持つ関数は、HandlerFunc型に変換できます。 HandleFuncオブジェクトにはServeHTTP
メソッドが組み込まれており、元の関数の内容を巧みかつ便利に実行するため、これは便利です。
それが混乱しているように聞こえる場合は、関連するソースコードを見てみてください。 これは、関数がハンドラーインターフェイスを満たすようにする非常に簡潔な方法であることがわかります。
この手法を使用してtimeHandlerアプリケーションを再現しましょう:p>
package mainimport ( "log" "net/http" "time")func timeHandler(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(time.RFC1123) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() // Convert the timeHandler function to a HandlerFunc type th := http.HandlerFunc(timeHandler) // And add it to the ServeMux mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
実際には、関数をHandlerFunc型に変換し、このようにServeMuxに追加することは非常に一般的であり、Goはショートカットを提供します。mux.HandleFunc
メソッド。
これは、このショートカットを代わりに使用した場合、main()
関数が次のように見えたものです。
func main() { mux := http.NewServeMux() mux.HandleFunc("/time", timeHandler) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
このような関数をハンドラとして使用することはほとんどの場合、うまく機能します….. しかし、物事がより複雑になり始めるときには少し制限があります。
以前の方法とは異なり、timeHandler
main()
から情報や変数をハンドラに渡したい場合はどうなりますか?
きちんとしたアプローチは、ハンドラロジックをクロージャに入れ、使用したい変数を閉じることです。
package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { mux := http.NewServeMux() th := timeHandler(time.RFC1123) mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}
timeHandler
関数は微妙に異なる役割を持つようになりました。 関数をハンドラに強制するのではなく(以前のように)、ハンドラを返すためにそれを使用しています。 この作業を行うには2つの重要な要素があります。
最初にfn
‐にアクセスする無名関数を作成するか、format
format
func(http.ResponseWriter, *http.Request)
という署名があります。 以前から覚えているように、これはHandlerFunc型に変換できることを意味します(Handlerインターフェイスを満たすように)。 私たちのtimeHandler
関数は、この変換されたクロージャを返します。
この例では、単純な文字列をハンドラに渡しています。 しかし、実際のアプリケーションでは、このメソッドを使用してデータベース接続、テンプレートマップ、またはその他のアプリケーションレベルのコンテ これは、グローバル変数を使用する良い代替手段であり、テストのためのきちんとした自己完結型ハンドラを作るという追加の利点を持っています。また、次のように書かれた同じパターンが表示される場合があります。
func timeHandler(format string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) })}
または、戻り時にHandlerFunc型への暗黙:
func timeHandler(format string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) }}
DefaultServeMux
最も単純なHello Worldの例からGoのソースコードまで、DefaultServeMuxが多くの場所で言及されているのを見たことがあこんにちこんにちこんにちこんにちこんにちこんにちこんにちこんにちこんにちこんにちこんにちこんにちはそれは特別なものではないことを認識するのに長い時間がかかりました。
DefaultServeMuxは、すでに使用しているような単純なol’ServeMuxであり、HTTPパッケージが使用されるときにデフォルトでインスタンス化されます。 Goソースからの関連する行は次のとおりです:
var DefaultServeMux = NewServeMux()
セキュリティ上のリスクがあるため、一般的にDefaultServeMuxを使用しないでください。
DefaultServeMuxはグローバル変数に格納されているため、アプリケーションがインポートするサードパーティパッケージを含む、どのパッケージもそれにアクセスしてルートを登録することができます。 これらのサードパーティのパッケージのいずれかが侵害された場合、DefaultServeMuxを使用して悪意のあるハンドラーをwebに公開する可能性があります。経験則として、DefaultServeMuxを避け、これまでのように独自のローカルスコープのServeMuxを使用することをお勧めします。 しかし、あなたがそれを使用することにした場合。..
HTTPパッケージには、DefaultServeMux:httpを操作するためのショートカットがいくつか用意されています。ハンドルとhttp。ハンドルファンク… これらは、すでに見てきた同名の関数とまったく同じですが、作成したものではなくDefaultServeMuxにハンドラーを追加するという違いがあります。
さらに、ListenAndServeは、他のハンドラが提供されていない場合(つまり、第二のパラメータがnil
に設定されている場合)、DefaultServeMuxの使用にフォールバックします。最後のステップとして、timeHandlerアプリケーションを更新して、代わりにDefaultServeMuxを使用しましょう。
package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { // Note that we skip creating the ServeMux... var format string = time.RFC1123 th := timeHandler(format) // We use http.Handle instead of mux.Handle... http.Handle("/time", th) log.Println("Listening...") // And pass nil as the handler to ListenAndServe. http.ListenAndServe(":3000", nil)}
このブログ記事を楽しんだ場合は、Goでプロのwebアプリケーションを構築する方法についての私の新しい本をチェッTwitter@ajmedwardsで私に従ってください。
この記事のすべてのコードスニペットは、MITライセンスの下で自由に使用できます。
div