🥟

【ルーティング】Goでchiする

2023/08/19に公開2

chi
https://go-chi.io/#/

背景

フレームワークを使うほどでもないが、ルーティングはできればライブラリに任せたい
ライブラリは下記もありますが、今回はchiを使用します
https://github.com/gorilla/mux

サンプル

  • 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