こんにちは。サーバサイドエンジニアをしている者です。
今回は副業でGoのechoサーバを使用していて、そこで詰まったことを解決する方法エントリーを書きます。
記事概要
echoサーバでRequestのBodyをechoのfuncのBind
で構造体にデコードする際に複数回使用できない。
使用すると
code=400, message=EOF
となりBind
ができない。
それの解決方法を書きます。
環境
version | |
---|---|
go | 1.12.5 |
OS | alpine3.10 |
echo | 3.3.10 |
本文
前提
curl http://localhost:8888/api/sellers -XPOST -H "content-type: application/json" -d '{"seller_type": 1, "email": "fhoirj@hogr.com", "name": "ssss", "uid": "edeedddeddd", "company_name": "234", "company_location": "jofhwf", "department": "部署", "tel": "01099992222", "consultation": "dhaofe", "industry_id": 1}'
このリクエストを投げるときに
seller_type
を取得してそのseller_type
からどのオブジェクトにデコードをするかを判断するということをしたかった。
素直にやるとすると
r.e.POST(path, func(c echo.Context) error {
dbHandler, ok := infra.ExecFromCtx(c)
if !ok {
return xerrors.New("db error")
}
sellerType := new(struct {
SellerType int `json:"seller_type"`
})
// ここでsellerTypeをbindする
if err := c.Bind(sellerType); err != nil {
return err
}
// sellerTypeによって振り分ける、それぞれのオブジェクトにデコードする
switch sellerType.SellerType {
case 1:
in := new(sellerusecase.CreateGeneralSellerInput)
if err := c.Bind(in); err != nil {
return err
}
return controller.NewSellerController(dbHandler).CreateGeneralSeller(c, in)
case 2:
in := new(sellerusecase.CreateEvalSellerInput)
if err := c.Bind(in); err != nil {
return err
}
return controller.NewSellerController(dbHandler).CreateEvalSeller(c, in)
default:
log.Fatal("fatal err")
}
}, filter.Transaction())
これを実行するとEOF
となり、エラーになる
解決方法
以下のようにする
r.e.POST(path, func(c echo.Context) error {
dbHandler, ok := infra.ExecFromCtx(c)
if !ok {
return xerrors.New("db error")
}
sellerType := new(struct {
SellerType int `json:"seller_type"`
})
// ここで一旦request bodyをbyte配列でもつ
bodyBytes, err := ioutil.ReadAll(c.Request().Body)
if err != nil {
return err
}
// sellerTypeをまずは取得する
if err := json.Unmarshal(bodyBytes, sellerType); err != nil {
return err
}
// ここで予め別のアドレスにおいておいた、request bodyをcontextのbodyに入れ直す
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
// ここでsellerTypeによって振り分けて、それぞれのオブジェクトでbindする
switch sellerType.SellerType {
case 1:
in := new(sellerusecase.CreateGeneralSellerInput)
if err := c.Bind(in); err != nil {
return err
}
return controller.NewSellerController(dbHandler).CreateGeneralSeller(c, in)
case 2:
in := new(sellerusecase.CreateEvalSellerInput)
if err := c.Bind(in); err != nil {
return err
}
return controller.NewSellerController(dbHandler).CreateEvalSeller(c, in)
default:
log.Fatal("fatal err")
}
}, filter.Transaction())
コードを見ればわかるかと思いますが説明すると、
// ここで一旦request bodyをbyte配列でもつ
bodyBytes, err := ioutil.ReadAll(c.Request().Body)
if err != nil {
return err
}
ここでまずrequest bodyをバイト列で別のアドレスに入れます。
続いて
// sellerTypeをまずは取得する
if err := json.Unmarshal(bodyBytes, sellerType); err != nil {
return err
}
// ここで予め別のアドレスにおいておいた、request bodyをcontextのbodyに入れ直す
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
sellerType
を先程格納したバイト配列から取得します。
その後ここが重要ですがc.Request().Body
に改めて先程のrequest bodyを格納します。
そうすることで再度Bind
できるようになります。