自分はWebFrameworkを使う時、データベースのインスタンス等はカスタムMiddlewareを使って開いたり閉じたりしてます。
最近echoを使ってみて、echoのカスタムMiddlewareのドキュメントってHandlerFuncを使って書いてあるですが、HandlerFuncだと引数が取れなくて困ってしまいました。
そんでソースを見てたらMiddlewareFuncを発見したって話しです。
前提条件
- golang
- 1.8
- WebFramework
- https://github.com/labstack/echo
- v3.1.0-rc.1
- MySQLドライバー
- https://github.com/go-sql-driver/mysql
- Version 1.3
カスタムコンテキストを定義
データベースのインスタンスはコンテキストに置くのでカスタムコンテキストを定義(参照:https://echo.labstack.com/guide/context)
package main
import (
"database/sql"
"github.com/labstack/echo"
)
// カスタムコンテキスト
type CustomContext struct {
echo.Context
DB *sql.DB
}
MiddlewareFuncの実装
早速、MiddlewareFuncを実装してみる。とりあえず、カスタムコンテキスト用とDB用とは分けて書いてみる。
package main
import (
"database/sql"
"github.com/labstack/echo"
_ "github.com/go-sql-driver/mysql" // 本当はmain.goに置いほうが良いらしい
)
// カスタムコンテキストを定義するMiddleware
func myCustomContextMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// カスタムコンテキストを初期化して次へ
cctx := &CustomContext{
Context: c,
}
return next(cctx)
}
}
}
// DBを設定するMiddleware
func myDBMiddleware(datasource string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cctx, ok := c.(*CustomContext)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "カスタムコンテキストが取得できません")
}
db, err := sql.Open("mysql", datasource)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "DBが取得できません")
}
defer db.Close()
// DBをコンテキストに設定して次へ
cctx.DB = db
return next(cctx)
}
}
}
main
後はMiddlewareを適用するだけ。
package main
import (
"context"
"database/sql"
"os"
"os/signal"
"syscall"
"github.com/labstack/echo"
)
func main() {
e := echo.New()
// カスタムコンテキスト用Middlewareを適用
e.Use(myCustomContextMiddleware())
// DB用Middlewareを適用
e.Use(myDBMiddleware("データソースは省略"))
errC := make(chan error)
go func() {
if err := e.Start(":8080"); err != nil {
errC <- err
}
}()
quitC := make(chan os.Signal)
signal.Notify(quitC, syscall.SiGINT, syscall.SIGTERM)
select {
case err := <-errC:
panic(err)
case <-quitC:
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := e.Shutdown(shutdownCtx); err != nil {
errC <- err
}
}
}
以上
今回カスタムコンテキスト用とDB用と分けたので必ず先にカスタムコンテキスト用を適用する必要があります。
DB用はDBを使うグループ(echo.Group)のみに適用したりすると良いかもしれません。