Issue submitting multipart/form data POST request using UnityWebRequest and IMultipartFormSection

I’m developing several apps which require interaction with a RESTful API via HTTP GET and POST requests. I had been successfully using UniWeb for these requests, but I am attempting to remove third party dependancies from my projects. I have been replacing the UniWeb calls calls with the new UnityWebRequest feature (Unity 5.4.0f3).

I have been having trouble building POST requests submitting multipart form data containing binary files. While I can get it to work using WWWForm, when I attempt to update to IMultipartFormSection implementations for future-proofing I consistently receive 400 errors from the server, saying that required fields are missing. When I look at the requests using WireShark, I can see that the fields are being sent as intended. However the Content-Type is being set as application/x-www-form-urlencoded. If I override this to multipart/form-data, I receive the same response from the server.

If anyone can point out what I’m doing wrong, or whether it is an issue in the UnityWebRequest implementation, I’d much appreciate it!

Here is the code where I construct and send the request, an example response, and the contents of an example multipart form exported from WireShark.

Code

                List<IMultipartFormSection> form = new List<IMultipartFormSection>();
                
                form.Add(new MultipartFormDataSection("device_id", SBSettings.DeviceId));
                form.Add(new MultipartFormDataSection("version", SBSettings.Version));
                form.Add(new MultipartFormDataSection("auth_token", MTSettings.AuthToken));
                
                if (!string.IsNullOrEmpty(currentItem.subject))
                    form.Add(new MultipartFormDataSection("subject", currentItem.subject));
                form.Add(new MultipartFormDataSection("username", currentItem.userName));
                
                if (MTGeoIP.LocationData.latitude != 0)
                    form.Add(new MultipartFormDataSection("user_lat", MTGeoIP.LocationData.latitude.ToString()));
                if (MTGeoIP.LocationData.longitude != 0)
                    form.Add(new MultipartFormDataSection("user_lng", MTGeoIP.LocationData.longitude.ToString()));
                if (!string.IsNullOrEmpty(MTGeoIP.LocationData.countryCode))
                    form.Add(new MultipartFormDataSection("country", MTGeoIP.LocationData.countryCode));

                form.Add(new MultipartFormFileSection("audio_file", fileRequest.downloadHandler.data, "audiofile.wav", "audio/wav"));
                
                UnityWebRequest request = UnityWebRequest.Post(url, form);
                request.SetRequestHeader("Accept","application/json");
                
                long sendTime = System.DateTime.Now.Ticks;
                yield return request.Send();

Response

{"error":true,"error_code":400,"error_string":"Validation errors.","errors":["The auth token field is required.","The device id field is required."]}

Request contents
POST /v1/recording HTTP/1.1
Host: —Redacted—
User-Agent: UnityPlayer/5.4.0f3 (UnityWebRequest/1.0, libcurl/7.46.0-DEV)
Accept-Encoding: deflate, gzip
Accept: application/json
X-Unity-Version: 5.4.0f3
Content-Length: 119649
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="device_id"
Content-Type: text/plain; encoding=utf-8

a65a9cfa-0512-49f3-9277-88f7cb569225GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="version"
Content-Type: text/plain; encoding=utf-8

FXO3jWqroShnTAZ9O76SCOsCWH7YymhDZzDZUquOVkb6PeSlB4bjbQBy3H7O8R6m/0.2GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="auth_token"
Content-Type: text/plain; encoding=utf-8

9FxYOIeI15LQStQ6GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="subject"
Content-Type: text/plain; encoding=utf-8

PluginTestGXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="username"
Content-Type: text/plain; encoding=utf-8

TestUserGXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="user_lat"
Content-Type: text/plain; encoding=utf-8

53.3GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="user_lng"
Content-Type: text/plain; encoding=utf-8

-6.2GXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: form-data; name="country"
Content-Type: text/plain; encoding=utf-8

IEGXKBCF0Z5V0MWdGLu4hVjddJoU8veT2TVIBdiITY
Content-Disposition: file; name="audio_file"; filename="audiofile.wav"
Content-Type: audio/wav

*Here be binary*

I have reported it:

https://fogbugz.unity3d.com/default.asp?826626_htlchp13nh8th2to

Also, I have experienced that UnityWebRequest does not build up the boundaries properly. The mandatory “–” are missing in the received request.