🔩 ねじき教室 Go と Web の教室

フレームワーク echo

読了目安 約3分

ここまでの標準ライブラリの知識がそのまま通用することを、echo で同じアプリを書き直して確かめます。

30秒でつかむ 書き方が短くなっても、HTTPの役割は同じ
書き方が短くなっても、HTTPの役割は同じnet/httpとechoは表面のAPIが違っても、ルーティング、ハンドラ、レスポンスの役割を共有します。1net/http標準ライブラリ2HTTPの役割中身は共通3echoフレームワーク
  1. net/http 標準ライブラリ
  2. HTTPの役割 中身は共通
  3. echo フレームワーク
この章の目次

ここまで 5 章で書いた一式を短く書けるようにまとめたライブラリが Web フレームワークです。 数ある中から echo を選ぶのは、近年の ISUCON で配布される Go の初期実装にたびたび採用されているからです。

同じアプリを echo で書く

次のコードを実行してください。

Go
package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

type Post struct {
	ID   int    `json:"id"`
	Body string `json:"body"`
}

func main() {
	e := echo.New()
	e.Use(middleware.Logger()) // アクセスログのミドルウェア

	e.GET("/hello", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello!")
	})

	e.GET("/users/:id", func(c echo.Context) error {
		id := c.Param("id")
		return c.JSON(http.StatusOK, map[string]string{"id": id})
	})

	e.POST("/posts", func(c echo.Context) error {
		body := c.FormValue("body")
		if body == "" {
			return c.String(http.StatusBadRequest, "本文 body が空です")
		}
		return c.JSON(http.StatusCreated, Post{ID: 1, Body: body})
	})

	e.Logger.Fatal(e.Start(":8080"))
}

実行後、REST クライアントで GET /helloGET /users/42POST /posts を送ってください。 POST は Content-Type に application/x-www-form-urlencoded を選びます。 表の bodyはじめての投稿 を入れます。

curl で同じリクエストを送る
シェル
curl http://localhost:8080/hello
curl http://localhost:8080/users/42
curl -X POST -d "body=はじめての投稿" http://localhost:8080/posts

サーバー側には、リクエストごとに所要時間などを含むログが出ます。 前章で自作したログミドルウェアの既製品が middleware.Logger() で、e.Use は「全ルートをこれで包め」という指定です。

標準ライブラリとの対比

やりたいことごとに並べると、一対一に対応しています。

やりたいこと標準ライブラリecho
ルート登録mux.HandleFunc("GET /hello", h)e.GET("/hello", h)
パスパラメータ"GET /users/{id}"r.PathValue("id")"/users/:id"c.Param("id")
クエリ文字列r.URL.Query().Get("q")c.QueryParam("q")
フォームの値r.FormValue("body")c.FormValue("body")
JSON を返すContent-Type を設定して json.NewEncoder(w).Encode(v)c.JSON(http.StatusOK, v)
ミドルウェアハンドラを包む自作関数e.Use(middleware.Logger())
起動http.ListenAndServe(":8080", mux)e.Start(":8080")

ハンドラの形だけは見た目が変わります。

Go
func(c echo.Context) error

別々の引数だった wr が、c という 1 つの入れ物にまとまっています。 失敗のたびに http.Error を呼ぶ代わりに、ハンドラが error を返せば echo がエラーレスポンスへの変換を引き受けます。

記法が {id} ではなく :id なのは、標準ライブラリにパスパラメータがなかった時代に、フレームワークが各自の記法を先に定めた名残です(ルーティングの章の注)。

フレームワークの下では同じことが起きている

echo は net/http を置き換えるものではなく、その上に載っています。 e.Start は内部で net/http のサーバーを起動し、リクエストごとに goroutine が走ります。 echo のルーター自体が http.Handler インターフェースを実装して、振り分けを担います。

だから、ISUCON で echo の初期実装を渡されても、e.GET の並びはルーティング、c.Param はパスパラメータ、e.Use はミドルウェアと、この Part の語彙で読み下せます。

ただし、投稿の内容はまだ変数の中にしかなく、サーバーを再起動すれば消えます。 データの仕舞い方は、次の Part 4 のデータベースと SQL で扱います。

手元で echo を使うとき

ページ内では echo を教材実行環境が用意します。 手元で同じコードを動かす段階では、外部パッケージを取得してから起動します。

シェル
go mod init example.com/echo-hello
go get github.com/labstack/echo/v4
go mod tidy
go run main.go