みえのじゆうちょう

ゆるふわエンジニャー

こんにちは世界が表示されるまで

ブラウザで「こんにちは世界」を表示してみよう的な処理を覚えたのでまとめ。
http.Handlehttp.HandleFunchttp.ListenAndServe周りのメモです。

Recipe

  1. パスのパターンと処理の組み合わせを登録
  2. サーバ処理を開始。
    (サーバはリクエストを受け取り、1で登録されたパターが存在すれば実行)

たったそれだけ。これをコードにすると

// 1. パスのパターンと処理の組み合わせを登録
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
http.Handle("/foo", fooHandler)

// 2. サーバの処理を開始
http.ListenAndServe(":8080", nil)

以上。

ですが、それぞれについてもう少し深く調べたので続けます。

1. パスのパターンと処理の組み合わせを登録

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
http.Handle("/foo", fooHandler)

サーバがリクエストを受け取った時、そのリクエストされたURLパスに応じてどういった処理を行うのかを登録する。
(この処理のことをハンドラ、と呼びます)

登録には以下の2種の関数を主に使用します。

  • http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))
  • http.Handle(pattern string, handler Handler)

どちらも基本同じで、第一引数にURLパスのパターンを指定します。
第二引数はhttp.HandleFuncは処理を直接指定しますが、http.HandleではHandler型を指定します。

Handler型

Handler型はInterfaceとしてhttpパッケージに定義されており、以下のメソッドを持った型をHandler型として扱うことができます。
ServeHTTP(ResponseWriter, *Request)

冒頭のサンプルにおけるfooHandlerは以下のように定義することができます。

type fooHandler {
    text string
}
func (f *fooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    f.text = "hello world"
    fmt.Fprintf(w, f.text)
}

Interfaceとは

type InterfaceName Interface{
    MethodName(Val) (retVal)
    ...
}

メソッドだけを定義した型のこと。
定義されたメソッドをすべて持っている構造体であればどのような型でもインタフェースとして扱うことができます。

URLパスと処理の登録先

http.ServeMux*1型の構造体に定義されます。
/path//path は同じとして判断され、どちらかがすでに登録されていた場合は、上書きされます。

2. サーバ処理を開始

http.ListenAndServe(":8080", nil)

この1行でサーバが起動し、待機状態になります。
リクエストを受け取り、処理のパターンを探し取り出す際にServeMux.Handler関数が呼び出されます。

参考&サンプルコード拝借

http - The Go Programming Language

(´・ω・`)

経験の浅さ所以かHandle or HandleFunc or Handlerという名前のややこしさに超悩まされました。

結局のところ、

  • Handleは登録
  • Handlerは取得

という感じで自分の中で落ち着きました。スッキリ。
いろいろ自己流での解釈なので間違ってるかもしれないので、見つけたら修正していく、という感じで…。
おそまつさまでございました。

*1:Multiplexer