Goでのリクエスト処理の要約

Last updated:21st January2018Filed under:golang tutorial

GoでのHTTPリクエストの処理は、主にServeMuxesとHandlersの2つです。

ServeMuxは本質的にHTTP要求ルーター(またはマルチプレクサ)です。 着信要求を事前定義されたURLパスのリストと比較し、一致するものが見つかるたびにパスに関連付けられたハンドラーを呼び出します。

ハンドラは、応答ヘッダーと本文を書く責任があります。 ほとんどのオブジェクトは、http.HandlerServeHTTP次の署名を持つメソッド:

ServeHTTP(http.ResponseWriter, *http.Request)

GoのHTTPパッケーとRedirectHandler。 簡単ではあるが工夫された例から始めましょう:

$ mkdir handler-example$ cd handler-example$ touch main.go

ファイル:メイン。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)}

これをすばやくステップしてみましょう:

  • mainhttp.NewServeMux関数を使用して空のServeMuxを作成します。
  • 次に、http.RedirectHandlerhttp://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))}

ここの正確なコードはあまり重要ではありません。

本当に重要なのは、オブジェクト(この場合はtimeHandlerServeHTTP(http.ResponseWriter, *http.Request)という署名を持つメソッドを実装していることです。 ハンドラーを作るために必要なのはそれだけです。

これを具体的な例に埋め込みましょう:

File:main。go

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

maintimeHandlertimeHandler&ポインタを生成するシンボル。 そして、前の例のように、mux.Handle関数を使用してこれをServeMuxに登録します。アプリケーションを実行すると、ServeMuxは/timetimeHandler.ServeHTTPメソッドに直接渡します。p>

先に行って試してみてください

: http://localhost:3000/timetimeHandlerを簡単に再利用できることにも注意してください。

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.HandlerFuncfunc(http.ResponseWriter, *http.Request)シグネチャを持つ関数は、HandlerFunc型に変換できます。 HandleFuncオブジェクトにはServeHTTPメソッドが組み込まれており、元の関数の内容を巧みかつ便利に実行するため、これは便利です。

それが混乱しているように聞こえる場合は、関連するソースコードを見てみてください。 これは、関数がハンドラーインターフェイスを満たすようにする非常に簡潔な方法であることがわかります。

この手法を使用してtimeHandlerアプリケーションを再現しましょう:p>

ファイル:メイン。go

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

このような関数をハンドラとして使用することはほとんどの場合、うまく機能します….. しかし、物事がより複雑になり始めるときには少し制限があります。

以前の方法とは異なり、timeHandlermain()から情報や変数をハンドラに渡したい場合はどうなりますか?

きちんとしたアプローチは、ハンドラロジックをクロージャに入れ、使用したい変数を閉じることです。

File:main。go

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‐にアクセスする無名関数を作成するか、formatformatfunc(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を使用しましょう。

File:main。go

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



コメントを残す

メールアドレスが公開されることはありません。