フレームワーク echo
読了目安 約3分
ここまでの標準ライブラリの知識がそのまま通用することを、echo で同じアプリを書き直して確かめます。
この章の目次
ここまで 5 章で書いた一式を短く書けるようにまとめたライブラリが Web フレームワークです。 数ある中から echo を選ぶのは、近年の ISUCON で配布される Go の初期実装にたびたび採用されているからです。
go get で導入する
echo は標準ライブラリではないので、外部パッケージとして取得します。 新しいディレクトリで次を実行してください。
mkdir echo-hello
cd echo-hello
go mod init example.com/echo-hello
go get github.com/labstack/echo/v4go get はパッケージをダウンロードし、依存していることを go.mod に記録します。
同じアプリを echo で書く
次の内容を main.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"))
}起動の前に依存関係を確定させます。
go mod tidy
go run main.gogo mod tidy は、実際に import しているパッケージに合わせて go.mod と go.sum(依存の記録ファイル)を整えるコマンドです。
省くと go run が missing go.sum entry というエラーで止まります。
外部パッケージを使うコードを書き換えたら go mod tidy、と覚えてください。
起動したら、別のターミナルから叩きます。
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") |
ハンドラの形だけは見た目が変わります。
func(c echo.Context) error別々の引数だった w と r が、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 で扱います。