前回、
の続きです。
Hello, World on HTTP
Go には簡易な Http サーバのパッケージがバンドルされています。
net/http
をインポートするだけで簡単に Http サーバを動かすことができます。
package main import ( "fmt" "log" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World") } func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil)) }
実行して http://localhost:8080/
にアクセスします。
簡単にHttpサーバを作れましたね。 では少し中身を見ていきましょう。
http.HandleFunc("/", handler)
で URLパターンとそれに応答する関数を指定しています。
この関数は以下のように定義されています。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
このように Go では関数をオブジェクトのように扱えます。
サーバの起動は http.ListenAndServe(":8080", nil)
としてリスンを開始します。
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
Go では変数定義を [変数名] [変数の型] として定義します。addr string
は addr
という名前の string
型の変数を意味します。
では関数の後ろにある error
とは何でしょうか。これは関数の戻り値の型を意味します。
error
は builtin パッケージに以下のようなインターフェースとして定義されています。
package builtin type error interface { Error() string }
Error()
という string
を返すメソッドを持った、何らかのインスタンス。となります。
では、私達の作った handler関数を見てみましょう。
func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World") }
w
と r
という引数を取り、それぞれ http.ResponseWriter
という型と *http.Request
という型となっています。
http.ResponseWriter
は先程の error
と同じようにインターフェースとして定義されています。
package http type ResponseWriter interface { // ... }
http.Request
は同じ http パッケージに struct型 として定義されています。
type Request struct { Method string // ... }
C言語の struct と同じように、他の型のコンテナとなります。メソッドの無いクラスですね(Go では struct を使った継承構造も定義できます)。
Request
や Method
の先頭が大文字なのは、struct やその属性をパッケージ外部に公開することを意味します。
小文字とした場合はパッケージの使用者側からは不可視になります。
話を戻して、*http.Request
の先頭にあるアスタリスクはC言語と同じくポインタ型を意味します。
Goでは引数は全て値渡しで引数が全てコピーされます。 呼び出し元で関数内で行った副作用の結果が必要な場合にはポインタでやり取りする必要があります。
REST サーバ
先程の例を少し変えて、JSON を返すサーバを作ってみましょう。
encoding/json
を使うことで JSON も簡単に扱うことができます。
最初に JSON の元となる struct型を定義します。
type User struct { Id int `json:"id"` Name string `json:"name"` Email string `json:"e-mail"` }
先程見た struct型 と異なり、型の後に何か付いていますね。 これはタグといい、Java で言うアノテーションのようなメタ情報を定義したものです。
今回利用する encoding/json
では、タグの情報からJSONのプロパティ名を作成してくれるようになっているので定義しています。
JSON文字列は json.Marshal()
を使って簡単に作成できます。
u := User{ Id : 1, Name : "Thome", Email : "thome@example.com"} json, err := json.Marshal(u) if err != nil { log.Fatal(err) }
struct型は Composite literals で初期化できます。つまり {}
で初期値を設定してインスタンスを生成できます。
:=
は通常 var u User
のように定義する変数ですが、型推論で暗黙定義するものです。
json.Marshal()
は戻り値を2つ返します。これを json, err
で受けています。err
を見てログ出力しています(Go ではif分の括弧は不要です)。
では先程の例を書き換えてみましょう。
package main import ( "log" "net/http" "encoding/json" ) type User struct { Id int `json:"id"` Name string `json:"name"` Email string `json:"e-mail"` } func handler(w http.ResponseWriter, r *http.Request) { u := User{ Id : 1, Name : "Thome", Email : "thome@example.com"} json, err := json.Marshal(u) if err != nil { log.Fatal(err) } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Write(json) } func main() { http.HandleFunc("/", handler) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("Error ListenAndServe : ", err) } }
実行して http://localhost:8080/
にアクセスします。
JSON 文字列が取得できましたね。
次回は、