golang + BlobStoreAPI で ERROR: blobstore: error reading next mime part with boundary "===============xxxxx==" (len=xx): multipart: NextPart: EOF のエラー

ERROR: blobstore: error reading next mime part with boundary "===============xxxxx==" (len=xx): multipart: NextPart: EOF


GAEのBlobStoreAPIにファイルをアップロードした後にリダイレクトされるエンドポイントで
以下のように blobstore.ParseUpload() したら・・・

ctx := appengine.NewContext(c.Request)
blobs, _, err := blobstore.ParseUpload(c.Request)

以下のエラーが出た。

ERROR: blobstore: error reading next mime part with boundary "===============xxxxx==" (len=xx): multipart: NextPart: EOF

原因は blobstore.ParseUpload(c.Request) を実行する前に ParseForm() を実行したから。
https://golang.org/pkg/net/http/#Request.ParseForm

ParseForm() は POST, GET のパラメータをパースするのに利用されるもので、
「It is idempotent.」とある通り、冪等な関数になっている。

冪等なので送信されたPOSTのデータを壊すことはないと思うが、
以下によると、blobstore.ParseUpload() を呼ぶ前に ParseForm() を実行すると、
blobstore.ParseUpload() で multipart が読めなくなるらしい。
https://github.com/astaxie/beegae/issues/15#issuecomment-64116485

なので、ParseForm() を実行する前に blobstore.ParseUpload() を実行してパースしておけばいい。

今回は以下のライブラリで実行されていたので原因を突き止めるまで少し苦労した。
r.FormValue() という関数が内部で ParseForm() を呼んでいる。
https://github.com/tommy351/gin-csrf/blob/master/csrf.go#L29

このライブラリは gin というWAFのミドルウェアで、csrfをチェックしてくれる。
リダイレクトされるエンドポイントでは自前でcsrfのチェックをするようにしたりして、
ミドルウェアが実行されないようにすれば ParseUpload() が正常に動作する。