私は Golang で Web 開発を勉強しています (初心者)。試してみたコードがいくつか見つかりましたが、なぜ動作するのかよくわかりません。ライブラリのソース コードとドキュメントを調べましたが、漠然としたアイデアしかなく、まだうまくいきません。以下のコードに注意してください。
package main
import (
"fmt"
"net/http"
)
type foo int
func (m foo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Some text")
}
func main() {
var bar foo
http.ListenAndServe(":8080", bar)
}
私が理解しているところによると、*を追加するとServeHTTP(w http.ResponseWriter, rhttp.リクエスト)関数メソッドとして、ハンドラインターフェース(私が正しく言っているならば)そして今フーは型ハンドラー私も理解していますhttp.ListenAndServeハンドラ型の入力を受け取るので、変数はバーが登場します。コードを実行してブラウザで localhost:8080 にアクセスすると、「Some Text」が表示されます。
質問:
これは具体的にどのように機能するのでしょうか? ServeHTTP 関数にはどのようにしてアクセスするのでしょうか?
ライブラリのソース コードを確認してみましたが、ServeHTTP がどのように動作するのか正確にはわかりませんでした。次の 2 つのコード (該当するかどうかはわかりません) を見つけましたが、これは関数を実装しているという印象を与えるものの、説明が必要です。
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
型名の後に関数がある HandlerFunc を使用した上記のような型宣言は見たことがありません。メソッドの宣言方法も見たことがありますが、上記のコードで何が起こっているのかわかりません。
ベストアンサー1
これは具体的にどのように機能するのでしょうか? ServeHTTP 関数にはどのようにしてアクセスするのでしょうか?
この質問に答えるには、どのようにhttp.ListenAndServe
機能するかを確認する必要があります。
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
ここでは、指定されたアドレスとハンドラーを使用してサーバーを作成し、ListenAndServer メソッドを呼び出します。それでは、そこを見てみましょう。
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
このメソッドは、指定されたアドレスでリッスンを開始し、新しく作成されたリスナーを使用して Server メソッドを呼び出すだけなので、その手順を追ってみましょう。
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
...
for {
rw, e := l.Accept()
...
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
Serve メソッドから、これが新しい接続を受け入れ、独自の goroutine で処理を開始するポイントであることがわかります。
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
for {
w, err := c.readRequest(ctx)
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
}
ここで、最終的に ServeHTTP メソッドを呼び出しますが、これはまだその関数の実装ではなく、標準ライブラリからのものであることがわかります。そこで、serverHandler 構造体に何が含まれているかを見てみましょう。
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
最終的に次のようになります。Handler を指定しなかった場合は、DefaultServeMux が使用されます。ただし、foo ハンドラーを指定したので、foo からの ServeHTTP が呼び出されます。
以上です。これらはすべて@でご覧いただけます。サーバー.go