HTTPサーバー #
注意事項 #
The Go Playground では HTTP サーバーが起動できない。
他の章に合わせて The Go Playground での実行リンクを記載してはいるが、
動作確認したい場合は Go が実行可能な環境にファイルをコピーして go run
などで実行すること。
HTTP サーバーを立ち上げる #
HTTP サーバーは net/http
パッケージの func ListenAndServe(addr string, handler Handler) error
を使って立ち上げる。
addr はサーバーのアドレスやポートを指定する。
handler はリクエストを受けてレスポンスを返すサーバー本体の処理を実装した値を指定する。
handler が nil の場合、http パッケージ組み込みのデフォルトのハンドラーが利用される。
このデフォルトのハンドラーはルーティングを担当するので、ユーザーはパスごとに起動するハンドラを定義し指定する。
package main
import (
"io"
"log"
"net/http"
)
func main() {
// Hello, world! とレスポンスを返すだけの HTTP ハンドラを定義する
h := func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Hello, world!\n")
}
// "/hello" へリクエストがあった場合に h を実行するようにデフォルトのルーターに設定する
http.HandleFunc("/hello", h)
// ポート 8080 でHTTP サーバーを起動する
log.Fatal(http.ListenAndServe(":8080", nil))
}
参考ドキュメント: net/http
HTTP サーバーのページごとの処理を登録する #
HTTP サーバーでは個々のページごとの処理を行うものを http.Handler
インターフェイスとして定義している。
http.Handler
はリクエストを受けてレスポンスを返す関数 ServeHTTP(w http.ResponseWriter, r *http.Request)
を持つ。
HTTP サーバーへ http.Handler
を登録するには http.Handle
関数を使用する。
package main
import (
"fmt"
"log"
"net/http"
)
type helloHandler struct{}
func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world\n")
}
func main() {
// "/hello" へリクエストがあった場合に helloHandler で処理を行うように登録する
http.Handle("/hello", new(helloHandler))
// HTTP サーバーの起動
log.Fatal(http.ListenAndServe(":8080", nil))
}
参考ドキュメント: net/http
HTTP サーバーのページごとの処理をする関数を登録する #
HTTP サーバーで個々のページごとの処理をする関数を登録するには http.HandleFunc
を使う。
http.Handle
は http.Handler
を実装する型を用意する必要があるが、http.HandleFunc
は
func(http.ResponseWriter, *http.Request)
という型の関数を用意するだけでいいので手軽である。
package main
import (
"fmt"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world\n")
}
func main() {
// "/hello" へリクエストがあった場合に helloHandler で処理を行うように登録する
http.HandleFunc("/hello", helloHandler)
// HTTP サーバーの起動
log.Fatal(http.ListenAndServe(":8080", nil))
}
参考ドキュメント: net/http
クエリストリングとフォームから値を取得する #
http.Request
の FormValue
メソッドを使うと、クエリストリングや POST, Put のフォームから値を取得できる。
同じフィールド名で複数の値を取得したい場合は、FormValue
では対応していないので、
データの参照元である http.Request
の Form
フィールドを直接利用する。
package main
import (
"fmt"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
fmt.Fprintf(w, "hello %s", name)
}
func main() {
http.HandleFunc("/hello", helloHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
/*
# curl コマンドでリクエストを送信してレスポンスを確認する
## GET リクエストでクエリストリングから値が取得できる
$ curl "http://localhost:8080/hello?name=aaa"
hello aaa
## POST リクエストでフォームのデータから値が取得できる
$ curl -F "name=bbb" "http://localhost:8080/hello"
hello bbb
*/
参考ドキュメント: net/http
クエリストリングとフォームから複数の値を取得する #
http.Request
の Form
フィールドには、クエリストリングや POST, Put のフォームデータが入っている。
Form
から値を取得したい場合は ParseForm
メソッドで初期化をする必要がある。
以下の例はパラメータ n
の値を全て足し合わせるハンドラを実装した。
package main
import (
"fmt"
"log"
"net/http"
"strconv"
)
func addHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
sum := 0
for _, v := range r.Form["n"] {
if vv, err := strconv.Atoi(v); err == nil {
sum += vv
}
}
fmt.Fprintf(w, "sum is %d", sum)
}
func main() {
http.HandleFunc("/add", addHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
/*
# curl コマンドでリクエストを送信してレスポンスを確認する
$ curl "http://localhost:8080/add?n=10&n=20"
sum is 30
*/
参考ドキュメント: net/http
HTTP リクエストメソッドごとに異なる処理をする #
GET や POST などの HTTP リクエストメソッドごとに異なる処理をしたい場合は http.Request
の Method
フィールドを参照して処理を分岐させる。
package main
import (
"fmt"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
fmt.Fprintf(w, "hello GET")
case http.MethodPost:
fmt.Fprintf(w, "hello POST")
case http.MethodPut:
fmt.Fprintf(w, "hello PUT")
case http.MethodDelete:
fmt.Fprintf(w, "hello DELETE")
default:
http.Error(w, "invalid method", http.StatusBadRequest)
}
}
func main() {
http.HandleFunc("/hello", helloHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
参考ドキュメント: net/http
POST リクエストのボディで JSON を受け取って処理する #
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
func jsonHandler(w http.ResponseWriter, r *http.Request) {
// POST 以外をエラーとする
if r.Method != http.MethodPost {
http.Error(w, "invalid method", http.StatusBadRequest)
return
}
// ボディからデータを取得
data, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// JSON に変換
var req map[string]interface{}
err = json.Unmarshal(data, &req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// JSON を利用してレスポンスする
fmt.Fprintf(w, "foo:%v, bar:%v", req["foo"], req["bar"])
}
func main() {
http.HandleFunc("/json", jsonHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
/*
# curl コマンドでリクエストを送信してレスポンスを確認する
$ curl -XPOST -d '{"foo":100, "bar":200}' 'http://localhost:8080/json'
foo:100, bar:200
*/
参考ドキュメント: net/http , encoding/json