net/httpを紐解いてみる
GoのHTTPサーバーを知るにはnet/httpパッケージの主要な型、インタフェースを押さえると見通しがよくなります。大きく以下の3つです。
- Handlerインタフェース
- HandlerFunc型
- ServeMux型
※ オライリー・ジャパン『実用Go言語』10.2.1 HTTPサーバーを実装する より
Handlerインタフェース
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
HandlerインタフェースはServeHTTPメソッドを定義しています。リクエストが呼び出されたときに動作するハンドラーです。Handlerインタフェースを満たしていれば、HTTPサーバーのハンドラーとしてふるまえます。
※ オライリー・ジャバン『実用Go言語』 10.2.1 HTTPサーバーを実装する より
なるほど、「Handlerインタフェースを満たす」ことがハンドラーとして振る舞うことの条件のようだ。
ここで、「特定のインタフェースを満たす」ことを「特定のインタフェースを満たすメソッドを実装する」こととして、間違って理解している気がしてくる。
Goのinterfaceについて今一度調べてみる
-
https://zenn.dev/mtskhs/articles/970cd5351e3257f5ad89
-
fmt.Printf(format string, a ...any) (n int, err error)
が内部で呼ぶ、String()メソッドは、Stringerインタフェースに宣言されているメソッド。 -
fmt.Printf(format string, a ...any) (n int, err error)
では、引数がStringerインターフェースを満たす型の場合はString()メソッドの結果を出力する- つまり、引数(a ...any)の型をレシーバとしてString()メソッドが実装されていることだと思われる
- https://pkg.go.dev/fmt#Stringer
-
-
https://pkg.go.dev/encoding/json#example-package-CustomMarshalJSON
-
MarshalJSON() ([]byte, error)
メソッドを特定の型をレシーバとして実装することで、encoding/json のMarshal(v any) ([]byte, error)
の振る舞いをカスタムすることができる話
-
- インタフェースとして定義した型で宣言したメソッドと同じシグネチャで任意の型をレシーバとするメソッドを実装
- 1にてメソッドを実装した任意の型をインターフェースとして定義した型に代入
1,2を通して、インターフェースの型に対して、メソッドが自動で実装される
HandlerFunc型
type HandlerFunc func(w ResponseWriter, r *Request)
- Goは構造体はもちろんのこと、関数型も型リテラルとして宣言することができる
-
net/httpでは
func(w ResponseWriter, r *Request)
をtype HandlerFunc
として定義している - https://pkg.go.dev/net/http#HandlerFunc
-
net/httpでは
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
-
net/httpには上記に加えて、
type HandlerFunc
をレシーバとしてServeHTTP(wq ResponseWriter, r *Request)
というシグネチャのメソッドがある- 処理内容として関数型のレシーバに対して与えられたResponseWriterと*Requestを引数に呼び出しを行うだけ
- https://pkg.go.dev/net/http#HandlerFunc.ServeHTTP
そのため、関数リテラルfunc(w ResponseWriter, r *Request)
e.g)
func Hello(w ResponseWriter, r *Request) {
w.Write([]byte("Hello"))
}
を作り、HandlerFuncでキャストすることで、
HandlerFunc(Hello)
Handlerインタフェースを満たすことができる
ここで
ここで、「特定のインタフェースを満たす」ことを「特定のインタフェースを満たすメソッドを実装する」こととして、間違って理解している気がしてくる。
といったモヤモヤに立ち返る。
「特定のインターフェースを満たすメソッドを実装する」ことを以下のような感じで理解していた
interfaceを満たすメソッドの実装例
package main
import "fmt"
type IFruits interface {
Name() string
Color() string
Suger() int
Sour() int
}
type Fluits struct {
N string
C string
Su int
So int
}
type Apple Fluits
func NewApple() IFruits {
return Apple{
N: "Apple",
C: "Red",
Su: 6,
So: 5,
}
}
func (a Apple) Name() string {
return a.N
}
func (a Apple) Color() string {
return a.C
}
func (a Apple) Suger() int {
return a.Su
}
func (a Apple) Sour() int {
return a.So
}
type Banana Fluits
func NewBanana() IFruits {
return Banana{
N: "Banana",
C: "Yellow",
Su: 9,
So: 1,
}
}
func (b Banana) Name() string {
return b.N
}
func (b Banana) Color() string {
return b.C
}
func (b Banana) Suger() int {
return b.Su
}
func (b Banana) Sour() int {
return b.So
}
type Orange Fluits
func NewOrange() IFruits {
return Orange{
N: "Orrange",
C: "Orange",
Su: 4,
So: 7,
}
}
func (o Orange) Name() string {
return o.N
}
func (o Orange) Color() string {
return o.C
}
func (o Orange) Suger() int {
return o.Su
}
func (o Orange) Sour() int {
return o.So
}
func main() {
ap := NewApple()
fmt.Println(ap.Color())
ba := NewBanana()
fmt.Println(ba.Suger())
or := NewOrange()
fmt.Println(or.Sour())
}
ServeMux型
mux := http.NewServeMux()
mux.Handle
mux.HandlerFunc()
http.ListenAndServe(":8080", mux)