HttpClient uploading MultipartFormData to play 2 framework
c#, http-post, multipartform-data, playframework-2.0, windows-phone-8
Solution
Here is the solution.. (hack)
There seems to be a problem with Play Framework when the boundary has quotes in it.
So i added the following code after multipart is created in order to remove them:
var content = new MultipartFormDataContent();
foreach (var param in content.Headers.ContentType.Parameters.Where(param => param.Name.Equals("boundary")))
param.Value = param.Value.Replace("\"", String.Empty);
Finally i had to add quotes "\"" manually to specific values on the header like the following:
Original: `Content-Disposition: form-data; name=attachment; filename=attachment.file` Changed to: `Content-Disposition: form-data; name="attachment"; filename="attachment.file"`
and
Original: `Content-Disposition: form-data; name=json` Changed to: `Content-Disposition: form-data; name="json"`
I don't think that its a mistake to have quotes or not anywhere in the header and maybe the parsing on play framework should be fixed accordingly.
Problem
I have the following code in a Windows Phone 8 project that uses RestSharp client: ``` public async Task<string> DoMultiPartPostRequest(String ext, JSonWriter jsonObject, ObservableCollection<Attachment> attachments) { var client = new RestClient(DefaultUri); // client.Authenticator = new HttpBasicAuthenticator(username, password); var request = new RestRequest(ext, Method.POST); request.RequestFormat = DataFormat.Json; request.AddParameter("json", jsonObject.ToString(), ParameterType.GetOrPost); // add files to upload foreach (var a in attachments) request.AddFile("attachment", a.FileBody, "attachment.file", a.ContType); var content = await client.GetResponseAsync(request); if (content.StatusCode != HttpStatusCode.OK) return "error"; return content.Content; } ``` Fiddler shows the generated header: ``` POST http://192.168.1.101:9000/rayz/create HTTP/1.1 Content-Type: multipart/form-data; boundary=-----------------------------28947758029299 Content-Length: 71643 Accept-Encoding: identity Accept: application/json, application/xml, text/json, text/x-json, text/javascript, text/xml User-Agent: RestSharp 104.1.0.0 Host: 192.168.1.101:9000 Connection: Keep-Alive Pragma: no-cache -------------------------------28947758029299 Content-Disposition: form-data; name="json" { "userId": "2D73B43390041E868694A85A65E47A09D50F019C180E93BAACC454488F67A411", "latitude": "35.09", "longitude": "33.30", "accuracy": "99", "maxDistance": "dist", "Message": "mooohv" } -------------------------------28947758029299 Content-Disposition: form-data; name="attachment"; filename="attachment.file" Content-Type: image/jpeg ?????JFIF??`?`?????C? $" &0P40,,0bFJ:Ptfzxrfpn????????np????????|????????????C"$$0*0^44^?p??????????????????????????????????????????????????????`?"?????????????? -------------------------------28947758029299 ``` The code above works fine on the Play2 API. However since the RestSharp does not seem to be stable I have decided to use the native HttpClient provided by Microsoft. Hence I wrote another function that uses HttpClient to do the same job: ``` public async Task<string> DoMultiPartPostRequest2(String ext, JSonWriter jsonObject, ObservableCollection<Attachment> attachments) { var client = new HttpClient(); var content = new MultipartFormDataContent(); var json = new StringContent(jsonObject.ToString()); content.Add(json, "json"); foreach (var a in attachments) { var fileContent = new StreamContent(new MemoryStream(a.FileBody)); fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "attachment", FileName = "attachment.file" }; fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(a.ContType); content.Add(fileContent); } var resp = await client.PostAsync(DefaultUri + ext, content); if (resp.StatusCode != HttpStatusCode.OK) return "error"; var reponse = await resp.Content.ReadAsStringAsync(); return reponse; } ``` The header that is generated from that code is the following: ``` POST http://192.168.1.101:9000/rayz/create HTTP/1.1 Accept: */* Content-Length: 6633 Accept-Encoding: identity Content-Type: multipart/form-data; boundary="e01b2196-d24a-47a2-a99b-e82cc4a2f92e" User-Agent: NativeHost Host: 192.168.1.101:9000 Connection: Keep-Alive Pragma: no-cache --e01b2196-d24a-47a2-a99b-e82cc4a2f92e Content-Type: text/plain; charset=utf-8 Content-Disposition: form-data; name=json { "userId": "2D73B43390041E868694A85A65E47A09D50F019C180E93BAACC454488F67A411", "latitude": "35.09", "longitude": "33.30", "accuracy": "99", "maxDistance": "dist", "Message": "test" } --e01b2196-d24a-47a2-a99b-e82cc4a2f92e Content-Disposition: form-data; name=attachment; filename=attachment.file Content-Type: image/jpeg ?????JFIF??`?`?????C? $" &0P40,,0bFJ:Ptfzxrfpn????????np????????|????????????C"$$0*0^44^?p????????????????????????????????????????????????????????"?????????????? --e01b2196-d24a-47a2-a99b-e82cc4a2f92e-- ``` So far so good. From my point of view the two headers seem to be identical. However when I debug the Play 2 API after executing `Http.MultipartFormData body = request().body().asMultipartFormData();` I noticed that the multipart data are not being parsed correctly. More specifically the multipart filed in the body variable is as follows: ``` MultipartFormData(Map(),List(),List(BadPart(Map(ntent-type -> text/plain; charset=utf-8, content-disposition -> form-data; name=json)), BadPart(Map()), BadPart(Map()), BadPart(Map()), BadPart(Map())),List()) ``` As you can notice it has several (actually 5 in this example) BadParts. Example: `BadPart(Map(ntent-type -> text/plain; charset=utf-8, content-disposition -> form-data; name=json))` Can anyone see what is going wrong here? Is the header generated by HttpClient wrong?