GoのGinでハンドラーに引数を渡したいとき

検索してたどり着いたみなさん、Gin やってますか？私は最近Go X Gin X LINE bot なサービスを作ろうとして結構悩みました。この経験を共有できたらいいな、と記事を書いています。

## 困ったこと：router.GET("/", handler.Index) に引数がない！

Goを始めてWebアプリを作ろうとしてGinに手を出した人には共感してもらえる（かもしれない）と思うんですが、私にはとても不思議に思えることがありました。アプリのルーティングを設定する場所に、Tour of Goとかでは見たことがない書き方が現れるのです。たとえば、こんな感じですね。

```go:main.go
func main() {
    router := gin.Defualt()
    ...
    router.GET("/", handler.Index)
    ...
}
```

素人の視点からすると、handler.Indexってなんなんだ！なんでカッコが付いてないんだ！とうならざるを得ません。そしてさらに悪いことに、LINE botを使おうとしていた私はドツボにハマることになります。LINE botはGoのためのSDKを用意していて、そのドキュメント（から類推するところ）によると最初に bot client instanceを作る必要があります。インスタンスは連発するとメモリ食いそうだから最初に呼び出したものを使いたい...

```go:main.go
func main() {
    bot, err := linebot.New(<channel secret>, <channel access token>)
    ...
}
```

うーん、困った。ハンドラーでbotのメソッドを使いたいのに、ハンドラーには引数がないからbotインスタンスを渡せない...

整理すると、

```go:main.go
func main() {
    router := gin.Default()
    bot, err := linebot.New(~~~, ~~~)
    ...
    router.GET("/", handler.Index) //ここにbotを渡したい!!!!!
}
```

（本筋に関係ない部分は適当に書いてます。また、```router.GET("/", handler.Index)```にbotを渡すことないだろう、というツッコミは甘んじて受け入れます。メソッドは```GET```でも```POST```でもなんでもいいです。ルーティングも ```/callback```とかの方が適切かもしれません。一つの具体例として多めに見てください。ハンドラーの名前も他にあるでしょう）←長い言い訳ですが、さて、この問題どう解決すればいいでしょうか？

## ハンドラーの型に注目する

```router.GET("/", handler.Index)```の部分は、パッケージを分けて書いてあるわけですが、一方で関数リテラルで書く方法もあります。

```go:main.go
router.GET("/", func (c *gin.Context) { ... })
```

一体どうなっているんだろう... ここで ```GET```メソッドの中身はどうなっているんだ、と調べてみます。とくにハンドラーの型がどうなっているんでしょう。

```go:routergruoup.go
...
// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle("GET", relativePath, handlers)
}
...
```

ハンドラーの部分は```handlers ...HandlerFunc```となっていますね。```...```が前につく型はN個（Nは０を含む自然数）の引数をとることができるというルールです。詳しくは[言語仕様](https://golang.org/ref/spec#Function_types "The Go Programming Language Specification - The Go Programming Language")を読んでみましょう。


では、型```HandlerFunc```とはなんなのか？その定義にさかのぼってみます。

```go:gin.go
...
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
...
```

簡潔な定義です。ここで明らかになるのは、```HandlerFunc```の型は関数型```func(*Context)```であるということです。（型```Context```はgin.goで定義される構造体です。他のパッケージから参照する場合は```gin.Context```と書きますので、この記事でも2つの書き方が現れます）。どうりで、ハンドラーを関数リテラルで書くときは必ず

```go:main.go
router.GET("/", func (c *gin.Context) { ... })
```

というふうに書くわけです。ここからわかることは、ハンドラーの型は```func(*Context)```でなければならない、ということです。

さて、私たちが書きたい関数```Xxx```はどんな形をしているでしょう。以下のようになって欲しいですね。

```go:main.go
func main() {
    ...
    bot, err := linebot.New()
    ...
    router.GET("/", handler.Xxx(bot))
    ...
}
```

関数```Xxx```の型を考えると、引数が```bot *linebot.Client```であり、戻り値が```func(*gin.Context)```でなければならない。つまり、関数```Xxx```の宣言は次のようになるでしょう。

```go:handler.go
func Xxx(bot *linebot.Client) func(*gin.Context) {
    ...
}
```

型で言うと```func(*linebot.Client) func(*gin.Context)```というふうになります。へんてこりんですね。

## ハンドラーの中身を考える

さて、ハンドラーの外側がわかってきたので、いよいよ中身を考えましょう。なんやこらー、とパソコンをぶん投げたくなりますが、順番に考えましょう。関数に戻り値があるということは、当然ですが、ブロックの中で```return```されるものがあります。簡単な関数の例を考えましょう。

```go
func succeess( x int) int {
    var y int
    y = x + 1
    return y
}
```

少し大げさに書きましたが、この場合は戻り値は```int```型であり、```return```されるのは```y```です。宣言されている通り、```y```は```int```型です。

同じように考えて、関数```Xxx```の戻り値は```func(*gin.Context)```型であるから、次のようなものが```return```されるはずです。

```go:handler.go
func Xxx(bot *linebot.Client) func(*gin.Context) {
    ...
    return func(c *gin.Context){ ... }
    ...
}
```

こうなれば簡単ですね。 ```return func(c *gin.Context){ ... }```のブロックの内部で```bot```を使うことができます。

## おわりに

LINE公式のGithubリポジトリーのサンプルはパッケージが1つしかありませんでしたため、パッケージを分ける技術を模索しました。次のサイトを参考にさせていただきました。最後ですがお礼申し上げます。

[Handlerに引数を追加したい時 - Qiita](https://qiita.com/koshi_an/items/f8c04b09bd27ee449971)

Ginのコードは公式リポジトリーから引用しました。
https://github.com/gin-gonic/gin

LINE bot 公式リポジトリー
https://github.com/line/line-bot-sdk-go