0. 超シンプルなAPIのコードを読む
main.go
package main
import (
"fmt"
"html"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
-
go run main.go
で実行 - ブラウザで
localhost:8080
を叩く。
1. 解読メモ
1-1. http.HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
- handlerを定義し、ServerMuxに登録する関数.
- handler : (リクエストURLのパターンマッチと、マッチしたリクエストに対する応答が定義)
- ServerMux : (パターンマッチで適合したリクエストに対して、そのパターンマッチを持つhandlerを呼び出してくれる)
1-2. http.ResponseWriter
- リクエストに対するレスポンスを作るためのインターフェース
1-3. *http.Request
- Serverが受け取ったhttpリクエストを扱うためにnet/httpパッケージで用意されている構造体
1-4. http.ListenAndServe
func ListenAndServe(addr string, handler Handler) error
- 第1引数で指定された[TCPアドレス:ポート]をListenし、第2引数で指定したhandlerを提供する。
- 上述のコードのように、handlerが
nil
を指定された場合はデフォルトのServerMuxが使われる。
2. 書き換え
- 以上を踏まえて次のように書き換えてみる
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", fooooServer)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func fooooServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}
- 単にhandlerに名前(fooooServer)を付けて、別途取り出してみただけ。でもコードが長くなる時などはこっちの方が分かりやすそう。。
3. その他のメモ、疑問解決
3-1. ResponseWriterインターフェースに値を入れるときに、fmt.Fprintfが使えるのはどうして?
- ResponseWriterの説明を読むと、以下のようになっている
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(int)
}
なので、素直に考えると、
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
ではなくて
w.Write([]byte("hello, " + r.URL.Path))
となる。
しかし、例えばstackoverflowのこのページでも議論されているように、Fprintfの第1引数はio.Writerというインタフェースが使われていて、これは以下のような作りになっているらしい。
type Writer interface {
Write(p []byte) (n int, err error)
}
つまりバイト型のスライスからstringへの変換は自動でやってくれるからfmt.Fprintfなどを使った方がベターなんだと。
3-2. Request構造体を見学
- サンプルをいじってRequest構造体を構成する要素を色々出力させて見る
type Request struct {
// For client requests an empty string means GET.
Method string
// URL specifies either the URI being requested (for server
// requests) or the URL to access (for client requests).
URL *url.URL
// The protocol version for incoming server requests.
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
// Header contains the request header fields either received
// by the server or to be sent by the client.
Header Header
// Body is the request's body.
Body io.ReadCloser
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
}
たくさんある。
fmt.Fprintf(w, "%q", html.EscapeString(r.Proto))
// HTTP/1.1と出る
fmt.Fprintf(w, "%q", html.EscapeString(r.Method))
// GETと出る。リクエストメソッドがnilだったらGETだと解釈している
Headerはmap構造になっている。
fmt.Fprintf(w, "%q", html.EscapeString(Header["Accept-Language"]))
// [en-US,en;q=0.5]と出る。
fmt.Fprintf(w, "%q", html.EscapeString(Header["Cache-Control"]))
// [max-age=0]と出る。
fmt.Fprintf(w, "%q", html.EscapeString(r.Close))
// falseと出る。レスポンスを返した後もコネクションクローズしない。
fmt.Fprintf(w, "%q", html.EscapeString(r.RequestURI))
// '/'と出る
fmt.Fprintf(w, "%q", html.EscapeString(r.Host))
// localhost:8080と出る