Go の net/http でシンプルな HTTP サーバーを立てる
Go の標準ライブラリ net/http を使えば、外部パッケージなしで HTTP サーバーを立ち上げられます。フレームワークに頼る前に、まずは標準ライブラリだけでサーバーを動かしてみましょう。
最小構成のサーバー
もっとも短いコードは以下のようになります。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}http.HandleFunc でパスとハンドラ関数を登録し、http.ListenAndServe でサーバーを起動しています。第 2 引数の nil は、デフォルトのマルチプレクサ(http.DefaultServeMux)を使うという意味です。
このコードを main.go として保存し、go run main.go を実行すると、http://localhost:8080 にアクセスできるようになります。ブラウザで開けば「Hello, World!」と表示されるはずです。
複数のルーティングを追加する
実用的なサーバーでは、パスに応じて異なるレスポンスを返す必要があります。HandleFunc を複数回呼べば、ルーティングを増やせます。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "トップページ")
})
http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "このサイトについて")
})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "OK")
})
fmt.Println("サーバー起動: http://localhost:8080")
http.ListenAndServe(":8080", nil)
}/health エンドポイントのように、w.WriteHeader でステータスコードを明示的に設定することも可能です。何も設定しなければ 200 OK がデフォルトで返ります。
ただし注意点があります。Go の DefaultServeMux はパスマッチングが単純で、/ は全パスにマッチしてしまいます。/about や /health に一致しないリクエストはすべて / のハンドラに流れる仕組みです。
リクエストメソッドで処理を分ける
REST API を作るなら、GET や POST などのメソッドごとに処理を分岐させたいところです。net/http ではメソッドのルーティングが組み込まれていないため、ハンドラ内で自分で分岐させます。
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
fmt.Fprintln(w, "ユーザー一覧を取得")
case http.MethodPost:
fmt.Fprintln(w, "ユーザーを新規作成")
default:
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
})r.Method でリクエストメソッドを取得し、switch で分岐するのが定番パターンになります。http.Error を使えば、エラーメッセージとステータスコードをまとめて返せます。
メソッドの分岐はハンドラ内で手動。自由度は高いが、ルーティングが増えると冗長になりやすい。
r.Get("/users", handler) のようにメソッドごとにルートを定義できる。大規模 API には向いている。
小規模なツールやプロトタイプであれば標準ライブラリで十分ですが、エンドポイントが 10 を超えてくるとフレームワークの導入を検討したほうがよいでしょう。
ListenAndServe のエラーハンドリング
http.ListenAndServe はエラーを返す関数です。本番コードではエラーを無視せず、適切にハンドリングしましょう。
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello!")
})
fmt.Println("サーバー起動: http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println("サーバーエラー:", err)
}
}よくあるのは「ポートがすでに使われている」というエラーです。別のプロセスが同じポートを占有していると起動に失敗します。その場合は lsof -i :8080 でプロセスを確認するか、ポート番号を変えて試してみてください。
log.Fatal を使うと、エラーメッセージを出力してプログラムを終了してくれます。サーバーの起動失敗は致命的なので、log.Fatal を使うのが一般的です。
log.Fatal(http.ListenAndServe(":8080", nil))標準ライブラリだけでもこれだけのことができます。まずはこの最小構成を動かしてみて、Go の HTTP サーバーの基本的な流れをつかんでみてください。