1
3

More than 1 year has passed since last update.

Go の net/http で Web サーバーを立てる

Last updated at Posted at 2022-06-21

基本のコード

マルチプレクサとして DefaultServeMux を使用し、http.HandlerFunc() でハンドラを付与していきます。

main.go
package main

import (
	"fmt"
	"net/http"
)

func hoge(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hoge")
}

func fuga(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "fuga")
}

func main() {
	server := http.Server{
		Addr:    ":8080",
		Handler: nil,
	}

	http.HandleFunc("/hoge", hoge)
	http.HandleFunc("/fuga", fuga)

	server.ListenAndServe()
}

解説

構造体 http.Server で Web サーバーを立ち上げる

構造体 http.Server の値に、Web サーバーの設定を記述することができます。
そして ListenAndServe() メソッドを呼び出し、Web サーバーを立ち上げます。

main.go
package main

import (
	"net/http"
)

func main() {
	server := http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
	server.ListenAndServe()
}

ハンドラを登録していないので、どこをアクセスしても 404 になります。

(厳密には Handler: nil のとき DefaultServeMux がハンドラとして使用されますが、DefaultServeMux はマルチプレクサなので「どのパスにどのようなハンドラを結びつけるか」を記述しなくてはなりません。)

単一のハンドラを登録する

構造体 http.Server のフィールド Handler は、 http.Handler 型を期待します。
この http.Handler はインターフェースです。

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

したがって、ServeHTTP(http.ResponseWriter, *http.Request のシグネチャを持つメソッドにより http.Handler を実装できます。

main.go
package main

import (
	"fmt"
	"net/http"
)

type HelloHandler struct{}

// *HelloHandler がインターフェース http.Handler を実装
func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello, world!")
}

func main() {
	// HelloHandler 型の変数を宣言
	handler := HelloHandler{}

	server := http.Server{
		Addr:    ":8080",
		Handler: &handler,
	}
	server.ListenAndServe()
}

こうしてハンドラを登録できたので、localhost:8080 の任意のパスにアクセスすると、`Hello, world!" と表示されるようになります。

http.Handle() で複数のハンドラを登録する

構造体 http.Server のフィールド Handlernil のとき、ハンドラとして DefaultServeMux が使用されます。

以下のように、DefaultServeMux は 構造体 ServeMux 型のポインタです。

type ServeMux struct {
	// 省略
}

var defaultServeMux ServeMux

var DefaultServeMux = &defaultServeMux

そして、*ServeMux は インターフェース http.Handler を実装しています。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	// 省略
}

すなわち、DefaultServeMuxhttp.Handler 型です。

この DefaultServeMux はマルチプレクサとして機能します。
関数 http.Handle() を用いて DefaultServeMux に複数のハンドラを付与していきます。

main.go
package main

import (
	"fmt"
	"net/http"
)

type HogeHandler struct{}
type FugaHandler struct{}

func (h *HogeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hoge")
}

func (h *FugaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "fuga")
}

func main() {
	hoge := HogeHandler{}
	fuga := FugaHandler{}

	server := http.Server{
		Addr:    ":8080",
		Handler: nil, // DefaultServeMux を使用
	}

	// DefaultServeMux にハンドラを付与
	http.Handle("/hoge", &hoge)
	http.Handle("/fuga", &fuga)

	server.ListenAndServe()
}

localhost:8080/hoge にアクセスすると "hoge"、localhost:8080/fuga にアクセスすると "fuga" と表示されます。

このとき、関数 http.Handle()DefaultServeMux のメソッド Handle() を呼び出しています。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

http.HandleFunc() で複数のハンドラを登録する

関数 http.HandleFunc() を使用すると、もっと簡単にハンドラを登録できます。
関数 http.HandleFunc() は第二引数に HandlerFunc 型の関数を期待します。

main.go
package main

import (
	"fmt"
	"net/http"
)

// HandlerFunc 型の関数を定義
func hoge(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hoge")
}

// HandlerFunc 型の関数を定義
func fuga(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "fuga")
}

func main() {
	server := http.Server{
		Addr:    ":8080",
		Handler: nil, // DefaultServeMux を使用
	}

	// HandlerFunc を Handler に変換
	// Handler を DefaultServeMux に付与
	http.HandleFunc("/hoge", hoge)
	http.HandleFunc("/fuga", fuga)

	server.ListenAndServe()
}

関数 http.HandleFunc()DefaultServeMux のメソッド HandleFunc() を呼び出しています。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

そして、DefaultServeMux のメソッド HandleFunc() は、 DefaultServeMux のメソッド Handle() を呼び出しています。

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

このとき、HandlerFunc(handler)http.Handler を実装しています。
ここで HandlerFunc 型の定義を確認してみましょう。

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

HandlerFunc 型にはメソッド ServeHTTP(http.ResponseWriter, *Request) が定義されています。
したがって HandlerFunc 型で関数をキャストすると、キャストした結果は http.Handler を実装することになります。

ServeMux によるルーティング

パスの末尾に / がない場合、完全一致でルーティングされます。
パスの末尾に / がある場合、以下のようなルーティングとなります。

main.go
package main

import (
	"fmt"
	"net/http"
)

func index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "index")
}

func hoge(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hoge")
}

func hogefuga(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hogefuga")
}

func main() {
	server := http.Server{
		Addr:    ":8080",
		Handler: nil,
	}

	http.HandleFunc("/", index)
	http.HandleFunc("/hoge/", hoge)
	http.HandleFunc("/hoge/fuga/", hogefuga)

	server.ListenAndServe()
}
入力パス 結果
/ index
/random index
/hoge hoge
/hoge/fuga hogefuga
/hoge/123/fuga hoge
/hoge/123/fuga/456 hoge
/hoge/fuga/456 hogefuga

また、/users/123/posts/456 のような URL からパスパラメータを取り出す機能はないので、自前で処理を書くか、他のライブラリやフレームワークを使用する必要があります。

参考

次回

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3