Go net/httpの使い方
net/http
パッケージはクライアントとサーバ側の両方の機能を提供している.一旦,サーバ側の理解を深める.流れとしては,クライアント
⇔マルチプレクサ
⇔ハンドラ
のようなイメージを持っておくと良さそう.マルチプレクサとハンドラはどちらもサーバ側の話である.net/http
パッケージはマルチプレクサのインターフェースに加え,デフォルトで使用されるマルチプレクサも提供している.
サーバーを建てる際には,ListenAndServeを使用すればよく,第一引数にネットワークアドレスを,第二引数にリクエストを処理するハンドラを使用すれば良い.ネットワークアドレスが空の場合,ポート80が設定され,ハンドラがnil
の場合,デフォルトのマルチプレクサDefaultServerMux
(URLに応じてリクエストを各ハンドラに転送する特殊なハンドラ)が使用される.(ハンドラについては後のコメントで書く.)
Server
構造体を使用することでサーバの設定を行える.
type Server struct {
Addr string
Handler Handler
DisableGeneralOptionsHandler bool
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
HTTP2 *HTTP2Config
Protocols *Protocols
}
package main
import (
"net/http"
)
func main() {
http.ListenAndServe("", nil)
}
上記コードをServer
構造体を使用して設定してみると以下のようにかける.
サーバに追加の設定が必要な際は適宜追加すると良さそう.
package main
import (
"net/http"
)
func main() {
server := http.Server{
Addr: "127.0.0.1:80",
Handler: nil,
}
server.ListenAndServe()
}
サーバーに機能を持たせるためにハンドラが必要.GolangにおけるハンドラはServeHTTP
メソッドを持ったインターフェースである.
ServeHTTP(http.ResponseWriter, *http.Request)
ServeMux
やDefaultServeMux
はServeHTTP
メソッドを持っているため,ListenAndServe
のハンドラとして使用できている.
ListenAndServe
について,ServeHTTP
メソッドを持っているハンドラを第二引数に指定することができるので,マルチプレクサではないハンドラを登録することもできる.(ただし,URLのマッチング処理がないため,すべてのリクエストが同じハンドラに転送される.自分には使用場面がわからない)
ServeHTTP
メソッドを持つ構造体を作成して,複数のハンドラを登録してみる.http.Handle
関数を使用することでハンドラをマルチプレクサに登録する.
package main
import (
"net/http"
)
type HelloHandler struct {}
func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}
type ByeHandler struct {}
func (h *ByeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Goodbye, World!"))
}
func main() {
server := http.Server {
Addr: ":8080",
}
helloHandler := HelloHandler{}
http.Handle("/hello", &helloHandler)
byeHandler := ByeHandler{}
http.Handle("/bye", &byeHandler)
server.ListenAndServe()
}
Golangにはハンドル関数と呼ばれるものがある.ハンドラ関数はハンドラのメソッドであるServeHTTP
と同じようなシグネチャを持つ関数であり,ハンドラのように振る舞うことができる.ハンドラ関数を用いることで簡単にハンドラを登録することができる.
package main
import (
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}
func bye(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Goodbye, World!"))
}
func main() {
server := http.Server {
Addr: ":8080",
}
http.HandleFunc("/hello", hello)
http.HandleFunc("/bye", bye)
server.ListenAndServe()
}
上記の例について,以下のリクエストを送ると404が返ってくる.
curl -X GET "http://localhost:8080/hello/ei"
404 page not found
では,以下のような変更を加えてみる.
+ http.HandleFunc("/hello/", hello)
- http.HandleFunc("/hello", hello)
すると,結果は以下のようになる.
curl -X GET "http://localhost:8080/hello/ei"
Hello, World!⏎
末尾が/
ではないURLについて,ServeMuxは完全一致するものを探す.末尾が/
である場合は,URLの前方一致するものを探す.そのため,URLをhello
からhello/
にしたことで結果が変わった.