GoのEchoでリクエストのパスパラメータ・クエリパラメータ・配列を取得する方法

開発Tips

※あくまでカモネギ用の備忘録なので、記述を簡素化する過程でミス等ありましたらすみません😭

適切なAPI定義の場合

パラメータならびにリクエストボディ

  • パスパラメータ : param1, param2
  • クエリパラメータ : param3
  • リクエストボディ : contents(配列)

curl例

curl -X 'POST' -i \
  'http://example.com/path/param1/param2?query=param3' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "contents": [
    {
      "value1": "値10",
      "list1": ["リスト11","リスト12"]
    },
    {
      "value1": "値20",
      "list1": ["リスト21","リスト22","リスト23"]
    }
  ]
}'

リクエストを構造体に変換

type handler struct {}

type Request struct {
  Param1  string `param:"param1" validate:"required"`
  Param2  string `param:"param2" validate:"required"`
  Param3  string `query:"param3" validate:"required"`

  Contents []Content `json:"contents"`
}

type Content struct {
  Value1  string    `json:"value1" validate:"required"`
  List1   []string  `json:"list1"  validate:"required"
}

func (h *handler) Handle(ctx echo.Context) error {
  // リクエストをシンプルにバインドすればOK。
  request := &Request{}
  if err := ctx.Bind(request); err != nil {
    return err
  }
  if err := ctx.Validate(request); err != nil {
    return err
  }

  ...
}

Bind1回で構造体に変換できるので楽ちん。

やや難ありなAPI定義の場合

パラメータならびにリクエストボディ

  • パスパラメータ : param1, param2
  • クエリパラメータ : param3
  • リクエストボディ : 配列(key無し)

難ありな点は、リクエストボディの配列に名前が付いていない点です。

上記の適切なAPI定義とは異なり、”contents”が付与されていません。

curl例

curl -X 'POST' -i \
  'http://example.com/path/param1/param2?query=param3' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '[
    {
      "value1": "値10",
      "list1": ["リスト11","リスト12"]
    },
    {
      "value1": "値20",
      "list1": ["リスト21","リスト22","リスト23"]
    }
]'

リクエストを構造体に変換

type handler struct {}

type Request struct {
  Param1  string `param:"param1" validate:"required"`
  Param2  string `param:"param2" validate:"required"`
  Param3  string `query:"param3" validate:"required"`
}

type Contents []Content

type Content struct {
  Value1  string   `json:"value1" validate:"required"`
  List1   []string `json:"list1"  validate:"required"`
}

-----
func (h *handler) Handle(ctx echo.Context) error {
  // リクエストボディの(配列)にkeyがないので、
  // Bind関数内で配列も含めたjson.Unmarshalができないため、
  // パスパラメータとクエリパラメータを各々取得する。
  param1 := ctx.Param("param1")
  param2 := ctx.Param("param2")
  param3 := ctx.QueryParam("param3")
  if param1 == "" || param2 == "" || param3 == "" {
    // 必要に応じてエラー処理
  }

  // リクエストボディをバインド
  contents := Contents{}
  if err := ctx.Bind(&contents); err != nil {
    return err
  }
  for _, content := range contents {
    if err := ctx.Validate(content); err != nil {
      return err
    }
  }

  ...
}

所感

意図的でないのであれば、API定義にてリクエストボディの配列に名前が付けた方が、

  • サーバサイドの実装がシンプルにできる。
  • フロントエンド・サーバサイド共に、配列が何を表しているのが理解しやすい。

と思いました。

参考資料

以上

タイトルとURLをコピーしました