GolangでWebアプリを作るときのまとめ。三回に分けて実施します。 初回と二回目はpureなgolangのコアモジュールを使って書いてみます。 初回はhttpモジュールについて二回目はDBを使った適当なWebAppを作ります。 三回目はrevelというgolangでは有名なWAFを使って書いてみます。
まずはこのfuncを覚えましょう。 ListenAndServeは以下の様な構成を持ちます。
ListenAndServeはサーバを起動して、ハンドラを登録するための関数。
第一引数にアドレスを、第二引数にハンドラを指定します。第二引数には基本的にnilを指定します。 一旦第二引数に関しては無視してください。 あとで説明します。
package main
import "net/http"
func main() {
http.ListenAndServe(":8080", nil)
}
$ go run src/main/main.go
$ curl http://localhost:8080/
=> 404 page not found
当然ながら何もレスポンスを返さないので、404 page not found が返ります。
ハンドラを登録して何かレスポンス返してみましょう。 リクエストやレスポンスを操作する関数として、HandleFunc関数があります。
HandleFunc関数は第一引数にhttp.ResponseWriter
を取り、第二引数に*http.Request
を受け取ります。
第一引数のResponseWriterがResponseを表しています。
第二引数のRequestを表しています。
package main
import (
"io"
"net/http"
)
// hello world, the web server
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
func main() {
http.HandleFunc("/hello", HelloServer)
http.ListenAndServe(":8080", nil)
}
$ curl http://localhost:8080/
=> 404 page not found
$ curl http://localhost:8080/hello
=> hello, world!
http.Request
はこんな構造
type Request struct {
Method string // GET, POST, PUT, etc.
URL *url.URL
// The protocol version for incoming requests.
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
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
}
んで、今回は、net/url.URLを使って動的なレスポンスを実現してみる。 URLはこんな構造。
type URL struct {
Scheme string
Opaque string // encoded opaque data
User *Userinfo // username and password information
Host string // host or host:port
Path string
RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
}
各値は
scheme://[userinfo@]host/path[?query][#fragment]
に相当。
サーバーを変更。Pathに応じてhelloに続く文字列が変わるようにした。
package main
import (
"fmt"
"io"
"net/http"
)
// hello world, the web server
func HelloServer(w http.ResponseWriter, req *http.Request) {
hello := fmt.Sprintf("hello, %q\n", req.URL.Path)
io.WriteString(w, hello)
}
func main() {
http.HandleFunc("/", HelloServer)
http.ListenAndServe(":8080", nil)
}
$ curl http://localhost:8080/dena
=> hello, "/dena"
ListenAndServeの第二引数がnilの場合、HandlerFuncなどで登録したHandlerが使われるが、第二引数にHandlerを指定した場合はそれがデフォルトのハンドラになり、以降ハンドラの追加、変更はできなくなる。
package main
import (
"fmt"
"io"
"net/http"
)
// hello worldだけ話す関数
func HelloServer1(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
// hello %pでパスを話す関数
func HelloServer2(w http.ResponseWriter, req *http.Request) {
hello := fmt.Sprintf("hello, %q\n", req.URL.Path)
io.WriteString(w, hello)
}
// DefaultHanderにするためのstruct
type AppHandler struct {
appName string
}
// HandlerはServeHTTPメソッドを実装する実体
func(index *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, %s!", index.appName)
}
func main() {
http.HandleFunc("/v1/", HelloServer1)
http.HandleFunc("/v2/", HelloServer2)
index := new(AppHandler)
index.appName = "sample app"
http.ListenAndServe(":8080", index)
}
$ curl http://localhost:8080/v1/
=> hello, sample app
$ curl http://localhost:8080/v2/abc
=> hello, sample app
$ curl http://localhost:8080/
=> hello, sample app
http://golang.org/pkg/net/http/