web-dev-qa-db-ja.com

ServeHTTPはどのように機能しますか?

私はGolang(初心者)でWeb開発を勉強しています。遊んだコードに出くわしましたが、なぜそれが機能するのかよくわかりません。ライブラリのソースコードとドキュメントを調べましたが、まだ漠然とした考えしかありません。 tクリックします。以下のコードに注意してください。

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、r * http.Request)を追加すると、ハンドラーインターフェイスが呼び出されます(正しく言っている場合)そして今fooタイプハンドラーでもあります。また、http.ListenAndServeが型ハンドラーの入力を受け取るため、変数barが機能することも理解しています。コードを実行してブラウザでlocalhost:8080に移動すると、「SomeText」が表示されます。

EDIT:インターフェースを実装しますは適切な用語であり、呼び出さないでください。

質問:

これはどのように正確に機能しますか?その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を使用した、上記のような型宣言を見たことがありません。メソッドがどのように宣言されているかも見てきましたが、上記のコードで何が起こっているのかわかりません。

6
DanT29

これはどのように正確に機能しますか?その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メソッドから、これが新しい接続を受け入れ、それ自体のゴルーチンで処理を開始するポイントであることがわかります。

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

ついに登場です。ハンドラーを指定しなかった場合はDefaultServeMuxが使用されますが、fooハンドラーを指定したため、foogetから呼び出されます。

以上です。これらはすべて server.go から見つけることができます

15
Henri Koski

GoのHTTPサーバーは、リッスンするアドレスとハンドラーを受け取ります。内部的には、TCPリスナーを作成して、指定されたアドレスで接続を受け入れます。要求が着信するたびに、次のようになります。

  1. 生のHTTPリクエスト(パス、ヘッダーなど)を_http.Request_に解析します
  2. 応答を送信するための_http.ResponseWriter_を作成します
  3. ServeHTTPメソッドを呼び出し、RequestResponseWriterを渡して、ハンドラーを呼び出します

ハンドラーは、Handlerタイプが行うfooインターフェースを満たすものであれば何でもかまいません。

_type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
_

標準ライブラリには、HandlerFunc(任意のfunc(ResponseWriter, *Request)を渡してHandlerとして使用できる)やServeMuxなどの便利な機能も含まれています。多くのHandlerを登録し、着信要求パスに基づいてどの要求を処理するかを選択できます。

4
Adrian

Net/httpの関連するタイプは

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

インターフェイスタイプ。このインターフェースを実装する具体的なタイプは、HTTPリクエストを処理するために使用できます。 barのタイプはfooであり、fooはHandlerインターフェースを実装します。組み込みのHTTPサーバーがリクエストを処理する必要がある場合は、バーのServeHTTPメソッドを呼び出します。

2
Volker

http.Handlerについて理解しようとしている人には、これらのブログ投稿を読むことをお勧めします。
Goでのリクエスト処理の要約
HTTPミドルウェアの作成と使用

このコードでの型宣言

type foo int

ハンドラーインターフェイスを実装しています。 ServeHTTPメソッドを実装するための構造体または任意の型を作成できます。お気に入り

type Foo struct{}
func (m foo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Some text")
}

ハンドラーインターフェイスまたはその他のインターフェイスを実装する場合インターフェイスで宣言されているすべてのメソッドを、ここでfooの型に実装する必要があります。

HandlerFuncServeHttpインターフェースのHandlerメソッドを実装しているため、ハンドラーとしてhttp.ListenAndServeに渡すことができます。 ServeHttpをラップするミドルウェアを作成するために使用できます。

HanlderFuncの詳細については、ターミナルでgodocを使用してください

godoc net/http HanlderFunc

0
Himanshu