JavaScript で FormData オブジェクトを使って送信されたデータを Go 側でパースする

Go で multipart/form-data を受信するには ParseForm() ではなく MultipartForm() を使う必要がある。

開発環境

  • go version go1.17.1 linux/amd64
  • react 17.0.2

本文

素のJavaScript でフォームデータを送信する方法を調べたところ FormData オブジェクトを使用するとよいと書いてあった。

XMLHttpRequest と FormData オブジェクトの使用

そのため FormData を使って送信し、Go で作った API 側で受信して Firestore に登録しようとしたのだが、中身が受信できなかった。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func inputHaldler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		err := r.ParseForm()
		if err != nil {
			log.Fatalln(err)
		}
		c := Content{
            // この Text が取れてない
			Text:    r.Form.Get("text"),
			Created: time.Now().UTC(),
			Updated: time.Now().UTC(),
		}
		_, _, err = client.Collection("posts").Add(ctx, c)
		if err != nil {
			log.Fatalln("An error has occurred: %s", err)
		}
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

原因を調べたところ、フォームの指定が x-www-form-urlencoded なのか multipart/form-data なのかで使用する関数が異なることがわかった。

Goのhttp.RequestでPostの値を取得する(ParseFormとParseMultipartFormの違いに注意しよう)

値がとれないとき、次の原因が考えられる。

  • ParseFormをしないでpostの値を取ろうとする
  • postする側(htmlまたはjs)がpostをmultipart/form-dataに指定している

フロントエンドに慣れていないとpostに種類があることを忘れる。multipart/form-dataの場合、GoはRequestをParseFormでなくParseMultipartFormで処理する。

まさにフロントエンドに慣れていなかったので post に種類があることを忘れていた。今回は FormData オブジェクトを使っていたため、multipart/form-data になっていた。

よって ParseForm ではなく ParseMultipartForm を使用する必要がある。

https://pkg.go.dev/net/http#Request.ParseMultipartForm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func inputHaldler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		err := r.ParseMultipartForm(1024 * 5)
		if err != nil {
			log.Fatalln(err)
		}
		c := Content{
			Text:    r.MultipartForm.Value["text"][0],
			Created: time.Now().UTC(),
			Updated: time.Now().UTC(),
		}
		_, _, err = client.Collection("posts").Add(ctx, c)
		if err != nil {
			log.Fatalln("An error has occurred: %s", err)
		}
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

今後

次回以降、Go のエラー処理方針とログ出力について今まで適当にやっていたので調べようと思う。