🛞

[Go] net/httpのHandlerを理解する。

2024/02/17に公開

はじめに

エンジニアを目指す大学生Tetsuです。
今回はgoバックエンドで初めに躓くHandlerについてまとめたいと思います。

参考にさせて頂いた記事
https://www.alexedwards.net/blog/an-introduction-to-handlers-and-servemuxes-in-go

Handlerとは

一言で答えましょう。HandlerとはServeHTTPを持つもの。

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

定義を読むとHandlerとは「ServeHTTP型関数をメソッドとして持つinterface」であるとわかります。

Handlerの役割

Handlerの定義は分かったけど、それに何の役割があるの?
Handlerの役割はServeHTTP関数を渡してあげることです。

これはリクエストに対してどんな手順で処理が進むのか考えれば分かりやすいかと思います。
以下のようなソースコードを考えると、リクエストを受けた時の手順はこうなります。

  1. ServeMuxがリクエストパス(/hello)に対応するHandlerを探す。
  2. 見つけたHandler(gh)のServeHTTPメソッドを呼び出す。
  3. ServeHTTPメソッド内の処理が実行される。

ここでHandlerが期待する処理が書かれたServeHTTPを渡してあげることにより、リクエストパスごとに処理ができるのです。

package main

import "net/http"

type GreetingHandler struct {
	name string
}

func (h *GreetingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello, " + h.name))
}

func main() {
	mux := http.NewServeMux()

	gh := &GreetingHandler{name: "World"}

	mux.Handle("/hello", gh)

	http.ListenAndServe(":8080", mux)
}
curl http://localhost:8080/hello
Hello, World⏎

全体像を見る

APIを作りたいので「複数のリクエストパスを用意して、パスごとに処理を割り当てる」ということをしたい訳です。そこで例を見ながら考えましょう。

登場人物を簡単にまとめると。。

  • http.ListenAndServe(string, http.Handler)
    第一引数のアドレスに第二引数のHundlerを割り当ててリクエストを受け取るサーバーを起動する関数。
  • ServeMux
    複数のパスにハンドラを登録できるようにするための分配器のようなもの。
  • Handler
    ServeHTTPを渡すもの
  • ServeHTTP
    レスポンスのための具体的処理を書いたメソッド
  • Handle(string, http.Handler)
    ServeMuxに対して第一引数のパスで第二引数のHundlerを登録するためのメソッド
package main

import "net/http"

type DogHandler struct {
}

type CatHandler struct {
}

func (h DogHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//DogHandlerのServeHTTPメソッド
}

func (h CatHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//CatHandlerのServeHTTPメソッド
}

func main() {
	mux := http.NewServeMux() //ServeMuxを定義

	dh := DogHandler{} //DogHandlerを定義
	ch := CatHandler{} //CatHandlerを定義

	mux.Handle("/dog", dh) //Helloを持つハンドラを登録
	mux.Handle("/cat", ch) //Goodbyeを持つハンドラを登録

	http.ListenAndServe(":8080", mux) //サーバーを起動
}

これで /dog/catに対してそれぞれの処理を割り当てられます。

もしServeMuxを使わないと。。?

ServeMuxを使わない場合はhttp.ListenAndServeに直接Handerを入れることになるのでHandlerは一つしか登録できません。つまり処理も一つしか定義できません。

ServeMuxは分配器、ルーターのようなイメージですかね。

package main

import "net/http"

type DogHandler struct {
}

func (h DogHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//DogHandlerのServeHTTPメソッド
}

func main() {
	dh := DogHandler{} //DogHandlerを定義

	http.ListenAndServe(":8080", dh) //サーバーを起動
}

最後に

最後まで読んでくださりありがとうございました!
今回は自分がnet/httpに入門した時に詰まったHandlerについて説明しました。個人的にはServeMuxとServeHTTPを一緒に考えれば理解しやすいと思います。Go歴の浅い大学生の書いた記事です。間違い等あれば指摘していただきたいです。

Discussion