Echoで複数回Bind可能なBinderを作る
背景
echoではBind構文によりstructにRequestのQueryやBody部をマッピングすることができる しかしながらBody部は2回目以降Bindしようとすると以下のようにErrorが発生する
"code=400, message=EOF, internal=EOF"
これを複数回Bindできるようにしようと言うのが今回の趣旨である
原因
HeaderやURL.Queryはmap型map[string][]string
で定義されているが、
Bodyはio.ReadCloser型なので読み取り開始位置の移動exp) f.Seek(0,0)
ができない
解決法
一度Body部を読み出してしまい、Read後に再度未使用のio.ReadCloser型のstructをBodyに代入するCustomBinderを作成する
環境
- golang 1.14
- echo v4系
CustomBinder作成
module/binder.go
package module import ( "bytes" "io/ioutil" "github.com/labstack/echo/v4" ) type CustomBinder struct { binder echo.DefaultBinder } func NewBinder() *CustomBinder { return &CustomBinder{ binder: echo.DefaultBinder{}, } } func (cb *CustomBinder) Bind(i interface{}, c echo.Context) (err error) { var b []byte if b, err = ioutil.ReadAll(c.Request().Body); err != nil { return } c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(b)) err = cb.binder.Bind(i, c) req := c.Request() req.Body = ioutil.NopCloser(bytes.NewBuffer(b)) c.SetRequest(req) return }
main.go
package main import ( "fmt" "net/http" "sample1/module" "github.com/labstack/echo/v4" ) func main() { // Echo instance e := echo.New() // customBinderでdefaultBinderを上書き e.Binder = module.NewBinder() // Routes e.POST("/", hello) // Start server e.Logger.Fatal(e.Start(":1323")) } type form struct { UserID int `json:"user_id"` Name string `json:"name"` } // Handler func hello(c echo.Context) error { f1 := form{} if err := c.Bind(&f1); err != nil { return c.JSON(http.StatusBadRequest, err.Error()) } fmt.Println("f1", f1.UserID) f2 := form{} if err := c.Bind(&f2); err != nil { return c.JSON(http.StatusBadRequest, err.Error()) } fmt.Println("f2", f2.UserID) return c.String(http.StatusOK, "Hello, World!") }
元の原因がgolangのhttp.Request().Body
の定義によるものなので、
別のFWでも考え方は使い回せると思う