【Go言語】ハンドラーの引数にrouter情報を渡したい

Bullet Group Advent Calendar 2020 13日目の記事です。

どうも、最近のモチベ低下と、業務で運用保守をメインにやるようになったという二つの理由から全くコードを書かなくなった3年目エンジニアです。
最近、環境の変化だったり自宅の改善だったりでやっとモチベ⤴︎⤴︎🔥🔥になってきたので、これから頑張るぞという意思で記事書いていきます。
大したことはしていないですが、少しでも気になった方がいたらチラッと読んでみてください。

f:id:ymitomo:20201212203149j:plain

目次

概要

最近、ブログっぽいものの自主制作を始めました。
その中でハンドラーをうまく使って、記事投稿時の処理でどうやって投稿した記事にroute情報を登録すれば良いかについて悩んでいました。

色々考えた結果、投稿時のハンドラーにrouterを引数として渡して、そこで書き上げた記事を登録しようと思いつきました。 イメージは下記のような感じです。(名前は許してください。)

// toukouRouteの中で投稿記事をハンドラー登録したい。
router.POST("/toukou", <処理 + routerを渡したい>)

今回は上記をどう実現するかについて、ハンドラーに紐づけた処理の中でさらにハンドラーを登録する方法の一つとして、引数にrouterを渡す方法を簡単に解説します。

内容

ハンドラーの使い方

今回はハンドラーの基礎的な使い方については特に解説しません。詳しくは以前書いた記事か公式ドキュメントを参照してください。(参考になりそうなQiitaの記事も貼っておきます)

blog.bltinc.co.jp

実装サンプル

先に実装の簡単なサンプルを載せます。(その他フロントや基礎的な部分は用意してください)
下記を実装した状態で、入力画面から投稿ボタンなどで投稿を完了すると、'/hoge'のパスを指定することでhoge.htmlが開かれるようになります。

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("views/*.html")
    router.Static("/assets", "./assets")
    router.POST("/toukou", toukouRoute(router))
    router.Run(":8080")
}

func sampleRegisterRoute(path string, router *gin.Engine) {
    router.GET(path, func(ctx *gin.Context) {
        ctx.HTML(http.StatusOK, "hoge.html", gin.H{})
    })
}

func toukouRoute(router *gin.Engine) func(*gin.Context) {
    return func(ctx *gin.Context) {
        ctx.Request.ParseForm()
        path := "hoge"
        // PutToRTDB()だったり
        // createHtml()だったり
        sampleRegisterRoute(path, router)
    }
}

pathの部分を上手い感じで使えば、記事ごとにrouteを登録できるということです。

解説

型をうまくイメージできれば理解できます。各メソッド・関数の引数と型を確認してみましょう。

1. POSTメソッドの引数を確認

まずrouter.POST()メソッドを調べてみると、引数となるのはstring型のpathとHandlerFunc型の引数が1つ以上であるということがわかります。

// POST is a shortcut for router.Handle("POST", path, handle).
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle(http.MethodPost, relativePath, handlers)
}

https://godoc.org/github.com/gin-gonic/gin#RouterGroup.POST

2. HandlerFunc構造体の型を確認

二つ目以降の引数となるHandlerFuncの型を確認すると、func(*Context)型であるということがわかります。

type HandlerFunc func(*Context)

https://godoc.org/github.com/gin-gonic/gin#HandlerFunc

3. 型を意識して実装

以上のことから、router.POST()メソッドは下記のように記述することができます。

router.POST("/", func(ctx *gin.Context) {})

そのため、routerを引数に持った、func(ctx *gin.Context)型の戻り値がある関数 or メソッドを作成することで、router.POST("/toukou", toukouRoute(router)) を表現できるようになりました。

func toukouRoute(router *gin.Engine) func(*gin.Context) {
    return func(ctx *gin.Context) {
        // 中身
    }
}

以上を踏まえた上でもう一度実装サンプルを見返してみてください。結構簡単なことだったんだ!と気づくことができると思います!

最後に

過去に3記事書いてるのでお暇あればみていただけると嬉しいです。

blog.bltinc.co.jp

blog.bltinc.co.jp

blog.bltinc.co.jp

リモートになってから半年以上かけて自宅環境を最適化している最中なので、今度暇があればこれについて書こうと思います。
この度は、貴重なお時間いただきありがとうございました。