🥟
【ルーティング】Goでchiする
chi
背景
フレームワークを使うほどでもないが、ルーティングはできればライブラリに任せたい
ライブラリは下記もありますが、今回はchiを使用します
サンプル
- chi無いとき
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}
- chiあるとき🥟
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
})
http.ListenAndServe(":8080", r)
}
DELETEのリクエストメソッドが来る場合、
- chi無いとき
r.Method == http.MethodDelete
のような条件文で処理しないといけないため、pjの規模によっては条件分岐が増えてきそうです。
package main
import (
"fmt"
"net/http"
)
func handleDelete(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
fmt.Fprint(w, "DELETE request received")
} else {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/delete", handleDelete)
http.ListenAndServe(":8080", nil)
}
- chiあるとき🥟
ifを使わずに待ち受けられるのでシンプルに書けそうです。
package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func handleDelete(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "DELETE request received")
}
func main() {
r := chi.NewRouter()
r.Delete("/delete", handleDelete)
http.ListenAndServe(":8080", r)
}
また、詳細ページposts/:postID/detail
を仮定すると、
- chiないとき
スラッシュでpathを分解して、各値を照合するロジックを書かなければいけないと思います。
package main
import (
"fmt"
"net/http"
"strings"
)
func handlePostDetail(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.URL.Path, "/")
if len(parts) != 4 || parts[1] != "posts" {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
postID := parts[2]
response := fmt.Sprintf("Detail of Post ID: %s", postID)
fmt.Fprint(w, response)
}
func main() {
http.HandleFunc("/posts/", handlePostDetail)
http.ListenAndServe("localhost:8080", nil)
}
- chiあるとき🥟
どういうルーティングで待ち受けているのかが明確です。
package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func handlePostDetail(w http.ResponseWriter, r *http.Request) {
postID := chi.URLParam(r, "postID")
response := fmt.Sprintf("Detail of Post ID: %s", postID)
fmt.Fprint(w, response)
}
func main() {
r := chi.NewRouter()
r.Get("/posts/{postID}/detail", handlePostDetail)
http.ListenAndServe(":8080", r)
}
他にも、、
- 正規表現を使用して明示的にpostIDの期待するフォーマットも書けます。
- サブルーターで繰り返しpathをハードコードする必要はありません。
- グループ化によって、ミドルウェアを使う/使わないを分けることができる
例:
- 許可するContentTypeの設定
- ダブルスラッシュの処理
- 受け付ける文字エンコーディングの設定
- CORS
- ヘルスチェック用のpath設定
- ロギング
- キャッシュ設定
- Oauth 2.0
- 末尾スラッシュの処理
- リクエスト数制限
- タイムアウト
- jwt
サンプル: https://github.com/go-chi/jwtauth/blob/master/_example/main.go
総括として、chiはリーダブルな書き方ができる & 多機能なミドルウェアの提供により、ルーティングの記述に時間を取られることなく効率的なコーディングが可能。
個人pjでも採用したいと思います。
ありがとうございました!
Discussion
追記: 下記のプロポーサルに期待
1.22で取り込まれた