🧭

LineBotのClientをContextで伝播させる

2022/10/05に公開約2,600字

普段はGoの学習をメインで頑張ってる人間です
https://github.com/lll-lll-lll-lll/line_connpass_bot

今回は途中で意気消沈したけど、message apiとconnpass api使ったbot作ってみた
全体像はこんな感じ

概要

  1. ユーザからLINEプラットフォームがメッセージ受け取る
  2. webhookでserverに通知
  3. connpass api叩く
  4. flex message作ってユーザに返す

linebot.ClientをContextに伝播させる

なぜcontext?

サンプルコードには直接HandlerFuncを満たす実装をしている
これでも正直全く問題はないと思う。ただ、個人的にはbotが色々なメソッドの引数や中で現れるのが嫌いなのでhttp.Requestのcontextに入れることにした
サンプル↓↓↓

https://github.com/line/line-bot-sdk-go/blob/master/examples/echo_bot/server.go

どのように書いたのか

  • LINEClientMiddlewareを挟んでその中でlinebot.Clientの初期化をして、contextのWithValueメソッドでcontextにClientを登録
  • LINEClientMiddleware内ではclientの初期化とついでに、署名を検証。LINEプラットフォームから送られたものか確認するのはここで行っている
  • 後はlineHandlerを使ってリクエストの処理
func main() {
	lineHandler := http.HandlerFunc(linecon.LINEWebhookHandler)
	http.Handle("/callback", linecon.LINEClientMiddleware(lineHandler))
	Run()
}

https://github.com/lll-lll-lll-lll/line_connpass_bot/blob/master/src/line.go

何を満たせばこんな感じの書き方できるか

モック


func TestMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("ミドル処理ゴニョゴニョ")
		fmt.Println("ミドル処理ゴニョゴニョ")
		fmt.Println("ミドル処理ゴニョゴニョ")
		next.ServeHTTP(w, r.WithContext(r.Context()))
	})
}

func WebhookHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("ハンド処理ゴニョゴニョ")
	fmt.Println("ハンド処理ゴニョゴニョ")
	fmt.Println("ハンド処理ゴニョゴニョ")
}

func main() {
	handler := http.HandlerFunc(WebhookHandler)
	http.Handle("/callback", TestMiddleware(handler))
	Run()
}


  • http.Handlerを引数に取る関数を定義
  • 返す型もhttp.Handler
  • return内にreturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){}とかく。
    これでHandlerFuncを満たすことになる。
    なぜならfunc(http.ResponseWriter, *http.Request)を別の方に定義し直したのがHanlerFuncだから。
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

これでServeHTTPを実行できる。このメソッドの中身はレシーバ先のHandlerFuncを実行してるだけ。
なので、最後の行

https://github.com/lll-lll-lll-lll/line_connpass_bot/blob/8ab444b88506703b389c91738ec8f49f135c1161/src/line.go#L42

ここで次のHandlerw http.ResponseWriterと contextを含んだr *http.Requestを渡して実行してる。

まとめ

contextを使う機会はあまりなかったのでいい勉強の機会になったと思う。

参考

Discussion

ログインするとコメントできます