【GO】ginでFormDataのPOSTリクエストをBindする

2 min read読了の目安(約1900字

概要

go-ginでサクッとRESTAPIを構築するの記事にある通り、ginではたいていの場合はjson形式で、 POSTのリクエストを受け取ると思います。ただ、multipart/form-data形式でファイルと合わせてリクエストの値を受け取る場合は、jsonではなくformの形式で値を受け取る必要が出てきます。
Reactでmultipart/form-dataを送信、Ginで受信するの記事にある通り、FormValueにjsonを文字列で入れたりとか、一個ずつ取り出して行っても良いのですが、せっかくなのでBindで対応できるかというのを少し調べてみたので書いてみたいと思います。

FormDataのバインドに関して

  • 構造体のフィールドに設定するタグはformを指定します。Ho to bind to slice values in go (gin) form?の記事が参考になります。
  • 階層構造があるオブジェクトを扱う場合は、form:"親フィールド名[子フィールド名]"のように構造体のタグを設定します。
  • 配列を扱う場合は、構造体のタグの末尾に[]を付けます。

実装サンプル

以下のようなデータをJavaScriptから送信して、ginで受け取ります。

sample.js
const submitData = new FormData();
submitData.append("id", "id1");
submitData.append("title", "testTitle");
submitData.append("category[]", "movie");
submitData.append("category[]", "life");
submitData.append("postInfo[summary]", "testSummary");
submitData.append("postInfo[contents]", "testContents");
submitData.append("attachmentFile", file);

golang側での実装は以下。

sampleModel.go

type SampleRequest struct {
	ID       string          `form:"id"`
	Title    string          `form:"title"`
	Category []string        `form:"category[]"`
	PostInfo PostInfoRequest `form:"postInfo"`
}
type PostInfoRequest struct {
	Summary  string   `form:"postInfo[summary]"`
	Contents string   `form:"postInfo[contetns]"`
}
handler.go
func SampleHandler(c *gin.Context) {
	var request SampleRequest
	// ファイル以外のリクエストの値をバインド
	err := c.Bind(&request)
	if err != nil {
		c.Status(http.StatusBadRequest)
	}
	// ファイルの取得
	file, fileHeader, err := c.Request.FormFile("attachmentFile")
	
	// 後続処理は割愛・・
}

その他

JavaScriptから階層があるオブジェクトや配列のFormDataを送信するやり方は、FormDataでオブジェクトの階層があるデータを送るを参照ください。