どうもてぃ。
ある程度キャッチアップを終えたので少しずつアウトプットしていきます。
go初心者なのでお手柔らかにお願いします。
環境
- Linux Mint 19.3 Cinnamon
- go version go1.14.4 linux/amd64
ホットリロード環境を作る
ホットリロードとしてairを使用します。
導入は簡単。GitHubに書いてあるとおり。
一応丁寧に書いておくと
$ go get -u github.com/cosmtrek/air
で、airを使えるようにしたあと、今回作成するファイル(main.go
)と同じディレクトリ内に.air.toml
を以下の形で配置するだけ。
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format # Working directory # . or absolute path, please note that the directories following must be under root. root = "." tmp_dir = "tmp" [build] # Just plain old shell command. You could use `make` as well. cmd = "go build -o ./tmp/main ." # Binary file yields from `cmd`. bin = "tmp/main" # Customize binary. full_bin = "APP_ENV=dev APP_USER=air ./tmp/main" # Watch these filename extensions. include_ext = ["go", "tpl", "tmpl", "html"] # Ignore these filename extensions or directories. exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"] # Watch these directories if you specified. include_dir = [] # Exclude files. exclude_file = [] # This log file places in your tmp_dir. log = "air.log" # It's not necessary to trigger build each time file changes if it's too frequent. delay = 1000 # ms # Stop running old binary when build errors occur. stop_on_error = true # Send Interrupt signal before killing process (windows does not support this feature) send_interrupt = false # Delay after sending Interrupt signal kill_delay = 500 # ms [log] # Show log time time = false [color] # Customize each part's color. If no color found, use the raw app log. main = "magenta" watcher = "cyan" build = "yellow" runner = "green" [misc] # Delete tmp directory on exit clean_on_exit = true
↑ 手順にあるexampleをそのまま使用すればおkです。
とりあえずairの導入は完了。
localhost:8080にアクセスできるようにする
まずは、net/http
パッケージを用いて8080ポートにアクセスできるようにしてみます。
http.ListenAndServe
を使うだけです。
package main import "net/http" func main() { http.ListenAndServe(":8080", nil) }
これでおk。
airで立ち上げて、localhost:8080
へアクセスすると…
なんにも設定してないので、デフォルトで404が返ってきます。
ListenAndServeを確認してみる
初心者の方は飛ばしても大丈夫です。
http.ListenAndServe
の定義を詳しく見ればわかるんですが、本当なら第二引数にはhandlerを渡します(厳密には違います)
// ListenAndServe always returns a non-nil error. func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
こんな感じ。
さらにhandlerが使われてるServerを見てみると…
// A Server defines parameters for running an HTTP server. // The zero value for Server is a valid configuration. type Server struct { // Addr optionally specifies the TCP address for the server to listen on, // in the form "host:port". If empty, ":http" (port 80) is used. // The service names are defined in RFC 6335 and assigned by IANA. // See net.Dial for details of the address format. Addr string Handler Handler // handler to invoke, http.DefaultServeMux if nil // TLSConfig optionally provides a TLS configuration for use // by ServeTLS and ListenAndServeTLS. Note that this value is // cloned by ServeTLS and ListenAndServeTLS, so it's not // possible to modify the configuration with methods like // tls.Config.SetSessionTicketKeys. To use // SetSessionTicketKeys, use Server.Serve with a TLS Listener // instead. TLSConfig *tls.Config . . .
http.DefaultServeMux if nil
らしいので辿っていくと
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) }
handlerがnilだったらDefaultServeMuxが入ってますね。
また、handlerに関して更にたどると
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones if mux.hosts { h, pattern = mux.match(host + path) } if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return }
handlerがnilだったらNotFoundHanlder()
が実行されてるっぽい。おそらくmux.matchj(path)
のところでパスが存在しない場合もNotFoundが動いてそうな処理が書かれてる。
見てみると…
// NotFound replies to the request with an HTTP 404 not found error. func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } // NotFoundHandler returns a simple request handler // that replies to each request with a ``404 page not found'' reply. func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
NotFoundが返されてる。
辿っていくと、handlerがnilの際、実際にどういう処理が行われてるか、なんとなくわかるはずです。
ある程度プログラミング経験のある人はgodocを確認しながらやっていくと理解が深まるんじゃないでしょうか。
↓godoc
http.HandlerFuncを使う方法
これが一番簡単。
package main import ( "net/http" "fmt" ) type handlerType int func (t handlerType) ServeHTTP(rw http.ResposeWriter, req *http.Request) { fmt.Fprintf(rw, "hello world") } func main() { var t handlerType http.ListenAndServe(":8080", t) }
main.go
として保存。
localhost:8080
にアクセスするとhello world
が表示されると思います。
パスに関しては設定していないので、どのパスにアクセスしても今の状態ではhello world
が表示されてしまいます。
↓こんな感じ
なので、それを改良していきます。
パスに応じて表示するものを変更する
ServeHTTP
のreq *http.Request
を使用します。
package main import ( "fmt" "net/http" ) type handlerType int func (t handlerType) ServeHTTP(rw http.ResponseWriter, req *http.Request) { switch req.URL.Path { case "/dog": fmt.Fprintf(rw, "dog dog dog") case "/cat": fmt.Fprintf(rw, "cat cat cat") default: fmt.Fprintf(rw, "default") } } func main() { var t handlerType http.ListenAndServe(":8080", t) }
かなり簡単ですが、req.URL.Path
でパスを取得して、switch
表示するものを変更しているだけです。
localhost:8080/dog
・localhost:8080/cat
・localhost:8080
にアクセスして確認してみるとswitchで評価されたパスによって表示されるものが変わるーといった感じです。
終わり
簡単すぎたので、次はhttp.HandleFunc
を使って良い感じにやっていければと思います。
今週中にその2書く予定ですのでお楽しみに。