I am trying to do a multipart call in C# from windows phone.
I am sending the exact same json of a working android call but from wp I am getting this response body:
"errorNumber":90 - A JSONObject text must begin with '{' at character 0 of "}
The first char I am sending is {.
Why does it happen?
Here is my code:
public async Task postHttpClient(string serviceUrl, string requestObj)
{
Debug.WriteLine("postHttpClient");
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "multipart/form-data");
Debug.WriteLine("requestObj: " + requestObj);
Debug.WriteLine("curly brace should be expected: " + (int)requestObj.Trim().ElementAt(0));
MultipartFormDataContent content = new MultipartFormDataContent();
var contentData = new StringContent(requestObj.Trim());
content.Add(contentData);
var responseVar = await client.PostAsync(serviceUrl, content);
responseVar.EnsureSuccessStatusCode();
Debug.WriteLine("responseVar: " + responseVar.ToString());
var body = await responseVar.Content.ReadAsStringAsync();
Debug.WriteLine("body: " + body);
}
catch (Exception e)
{
Debug.WriteLine("e: " + e.ToString());
}
}
and my fiddler:
Raw Headers
POST /MyServer/SendActivity/ HTTP/1.1
Accept: /
Content-Length: 703
Accept-Encoding: identity
Content-Type: multipart/form-data; boundary="e8763d7d-a53d-4baa-a6c7-c3bc37bd52a6"
User-Agent: NativeHost
Host: merp.techmobile.eu:8080
Connection: Keep-Alive
Pragma: no-cache
Textview tab
--e8763d7d-a53d-4baa-a6c7-c3bc37bd52a6
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data
{"SendActivityRequest":{"activity":{...
It might help someone to know that I fixed it by adding a key to my json,
By changing this line: content.Add(contentData);
to this content.Add(contentData, "request");
Related
formData.Add(sJobId,"job_id"); is sending "job_id\r\n" to the server
Here is my C# method:
public static async Task UploadAsync(string url, int job_id, string filename, string filePath) {
try {
// Submit the form using HttpClient and
// create form data as Multipart (enctype="multipart/form-data")
using (var fileStream = new StreamContent(System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read)))
using (var formData = new MultipartFormDataContent()) {
StringContent sJobId = new StringContent(job_id.ToString());
StringContent sOthId = new StringContent(job_id.ToString());
// Try as I might C# adds a CrLf to the end of the job_id tag - so have to strip it in ruby
formData.Add(sOthId, "oth_id");
formData.Add(sJobId,"job_id");
fileStream.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
formData.Add(fileStream, "dt_file", filename);
HttpResponseMessage response = await HttpClient.PostAsync(url, formData);
// If the upload failed there is not a lot we can do
return;
}
} catch (Exception ex) {
// Not a lot we can do here - so just ignore it
System.Diagnostics.Debug.WriteLine($"Upload failed {ex.Message}");
}
}
This is what my Ruby puma server is receiving - see how oth_id and job_id have \r\n appended but "dt_file" does not.
Parameters: {"oth_id\r\n"=>"42157", "job_id\r\n"=>"42157", "dt_file"=>#<ActionDispatch::Http::UploadedFile:0x007f532817dc98 #tempfile=#<Tempfile:/tmp/RackMultipart20190715-37897-189ztb6.msg>, #original_filename="2019-07-15 164600.msg", #content_type="application/octet-stream", #headers="Content-Type: application/octet-stream\r\nContent-Disposition: form-data; name=dt_file; filename=\"2019-07-15 164600.msg\"; filename*=utf-8''2019-07-15%20164600.msg\r\n">}
How do I stop the formData.Add appending a \r\n to the name?
The raw message the application is sending to the server is
POST https://example.com/ HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="93655e5a-b6b3-48d6-82c9-0d9aa99164cc"
Content-Length: 522
--93655e5a-b6b3-48d6-82c9-0d9aa99164cc
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=oth_id
1234
--93655e5a-b6b3-48d6-82c9-0d9aa99164cc
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=job_id
1234
--93655e5a-b6b3-48d6-82c9-0d9aa99164cc
Content-Type: application/octet-stream
Content-Disposition: form-data; name=dt_file; filename=myfile.txt; filename*=utf-8''myfile.txt
a,b,c,d
aa,"b,b","c
c",dd
aaa
--93655e5a-b6b3-48d6-82c9-0d9aa99164cc--
Have a look at the name values.
Looking at RFC 7578 I can see in every example, that the value for name is always quoted.
Content-Disposition: form-data; name="user"
I did not find any hint if it is mandantory or not to quote the values, so I cannot judge who is wrong here.
To get such quoted name values you only have to quote the values in code.
public static async Task UploadAsync(string url, int job_id, string filename, string filePath) {
try {
// Submit the form using HttpClient and
// create form data as Multipart (enctype="multipart/form-data")
using (var fileStream = new StreamContent(System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read)))
using (var formData = new MultipartFormDataContent()) {
StringContent sJobId = new StringContent(job_id.ToString());
StringContent sOthId = new StringContent(job_id.ToString());
// Try as I might C# adds a CrLf to the end of the job_id tag - so have to strip it in ruby
formData.Add(sOthId, "\"oth_id\"");
formData.Add(sJobId,"\"job_id\"");
fileStream.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
formData.Add(fileStream, "\"dt_file\"", filename);
HttpResponseMessage response = await HttpClient.PostAsync(url, formData);
// If the upload failed there is not a lot we can do
return;
}
} catch (Exception ex) {
// Not a lot we can do here - so just ignore it
System.Diagnostics.Debug.WriteLine($"Upload failed {ex.Message}");
}
}
which will now post this
POST https://example.com/ HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="c33cdc86-db44-40ef-8e6e-3e13a96218d1"
Content-Length: 528
--c33cdc86-db44-40ef-8e6e-3e13a96218d1
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name="oth_id"
1234
--c33cdc86-db44-40ef-8e6e-3e13a96218d1
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name="job_id"
1234
--c33cdc86-db44-40ef-8e6e-3e13a96218d1
Content-Type: application/octet-stream
Content-Disposition: form-data; name="dt_file"; filename=myfile.txt; filename*=utf-8''myfile.txt
a,b,c,d
aa,"b,b","c
c",dd
aaa
--c33cdc86-db44-40ef-8e6e-3e13a96218d1--
Just found a pull request for PowerShell
// .NET does not enclose field names in quotes, however, modern browsers and curl do.
contentDisposition.Name = $"\"{LanguagePrimitives.ConvertTo<String>(fieldName)}\"";
I've got a unit test which I'm trying to fix. All I need to do is return a valid 200 HttpResponseMessage with a batch response for a single query (A 404 will do). I'm new to OData and dealing with HTTPMessages in general. This is what I've done so far, but I'm not sure it's the right way to do things. Could you help me understand where I might be going wrong?
string content = string.Format(
#"--batch_{0}
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 404 Not Found
OData-Version: 4.0
Content-Type: application/json;odata.metadata=minimal;odata.streaming=true;charset=utf-8
Content-Length: 42",
batchCode);
content = content + Environment.NewLine + #"{ .error.:.not_found.,.reason.:.missing.}".
content = content + Environment.NewLine + Environment.NewLine + string.Format(#"--batch_{0}--", batchCode) + Environment.NewLine;
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(content, System.Text.Encoding.UTF8, "multipart/mixed")
};
Since this is the response the boundary must be: --batchresponse_{batchCode}.
You don't need to specify the OData-Version in the sub-responses, only in the header of the parent.
There needs to be a newline between the headers and the body (in your case before the HTTP/1.1 404 Not Found line).
There must be a newline between the headers and the body of the child-response (before the line w/ your json).
A complete response body may look something like this:
--batchresponse_4a21740c-169a-4442-b771-8989207e80e9
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
{"Some":"Json"}
--batchresponse_4a21740c-169a-4442-b771-8989207e80e9--
Also, the json in the response doesn't look like valid json to me, not sure if that's a problem.
string batchCode = this.GetBatchCode(requestContent);
var innerResponse = new HttpMessageContent(new HttpResponseMessage(HttpStatusCode.NotFound));
MultipartContent batchContent = new MultipartContent("mixed", "batch_" + batchCode);
innerResponse.Headers.Remove("Content-Type");
innerResponse.Headers.Add("Content-Type", "application/http");
innerResponse.Headers.Add("Content-Transfer-Encoding", "binary");
batchContent.Add(innerResponse);
var outerResponse = new HttpResponseMessage(HttpStatusCode.OK);
outerResponse.Content = batchContent;
return outerResponse;
I develop a Xamarin.Android app and I need to update an audio recorded file.
For this, my client has provided me an API like this:
URL: /api/recordings/
Method: POST
Data format: multipart/form-data
Fields: some text fields and the audio file to send
When I test it on Postman, it works fine. The generate code snippets are:
POST /api/recordings/ HTTP/1.1
Host: X.X.X.X
Cache-Control: no-cache
Postman-Token: xxxxx-xxxx-xxxx-xxxx-xxxxx
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="param1"
param1 name
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="param2"
param2 name
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="audio_file"; filename="xxx.wav"
Content-Type: audio/wav
But when I try from the app, I get an 400 - Bad Request error.
First attempt:
I first tried with the following basic code:
using (HttpClient client = new HttpClient())
{
try
{
MultipartFormDataContent formData = new MultipartFormDataContent();
formData.Add(new StringContent("param1 name"), "param1");
formData.Add(new StringContent("param2 name"), "param2");
var response = await client.PostAsync(url, formData);
Logger.Log("Response : " + responseObj.ToString());
}
catch (Exception ex)
{
Logger.Log("Exception : " + ex.ToString());
}
}
But there is a 400 - Bad Request error:
Response : StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Server: nginx/1.15.0
Date: Mon, 25 Jun 2018 23:25:41 GMT
Connection: keep-alive
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Content-Length: 85
Allow: POST, OPTIONS
}
Second attempt:
Then I've tried the following snippet that I found there:
string servResp = "";
string boundary = "----CustomBoundary" + DateTime.Now.Ticks.ToString("x");
using (var content = new MultipartFormDataContent(boundary))
{
content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
content.Add(new StringContent("param1 name"), "param1");
content.Add(new StringContent("param2 name"), "param2");
HttpClientHandler handler = new HttpClientHandler();
var cookieContainer = new CookieContainer();
handler.CookieContainer = cookieContainer;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.ExpectContinue = false;
request.Content = content;
try
{
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
servResp = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
Logger.Log("PostTest() - Exception : " + ex.ToString());
}
}
return servResp;
But there is still a 400 - Bad Request error:
Exception : System.Net.Http.HttpRequestException: 400 (Bad Request)
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode () [0x0002a] in <996a681f30a44cd685a4da54e11956e2>:0
=> Would you have any explanation? How could I fix this?
Edit : the audio file
I haven't specified the code of the audio file, I use a ByteArrayContent in both cases:
byte[] b = System.IO.File.ReadAllBytes(fullFilePath);
formData.Add(new ByteArrayContent(b, 0, b.Length), "audio_file", "audio.wav");
Conclusion:
I was able to resolve my problem by creating a console project that using the same service: this allows me to find and fix my error.
For an other issue, I followed the Fiddler instructions to parameter proxy on the device. However, it seems that the proxy doesn't work for apps but only for websites in the latest versions of Android...
Do you know an alternative to Fiddler that could work on Android devices?
I am trying to flash a spark core from a C# application. I keep getting { error: Nothing to do? } response.
Below is my code
var url = string.Format("https://api.spark.io/v1/devices/{0}", sparkDeviceID);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accesstoken);
using (var formData = new MultipartFormDataContent())
{
HttpContent fileContent = new ByteArrayContent(Encoding.ASCII.GetBytes(rom));
//client.SendAsync()
formData.Add(fileContent, "file", "file");
var response = client.PutAsync(url, formData).Result;
if (!response.IsSuccessStatusCode)
throw new Exception("An error occurred during rom flash!");
var responseStream = response.Content.ReadAsStreamAsync().Result;
using (var reader = new StreamReader(responseStream, true))
{
var result = reader.ReadToEnd();
}
}
return true;
}
The documentation reads:
The API request should be encoded as multipart/form-data with a file field populated.
I believe the problem is the endpoint doesn't see the file. Any idea on how to resolve this?
Finally got it working.
The issue was the way .NET generated the content-disposition header for the file form data.
I used fiddler to compare the output of a successful put request to the put request that my code was generating:
Successful PUT request generated using CURL:
PUT http://127.0.0.1:8888/ HTTP/1.1
User-Agent: curl/7.33.0
Host: 127.0.0.1:8888
Accept: */*
Content-Length: 2861
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------5efcf64a370f13c8
--------------------------5efcf64a370f13c8
Content-Disposition: form-data; name="file"; filename="ms.ino"
Content-Type: application/octet-stream
...
My PUT request (unsuccessful):
PUT https://api.spark.io/v1/devices/{deviceid} HTTP/1.1
Authorization: Bearer {access_token}
Content-Type: multipart/form-data; boundary="135f5425-9342-4ffa-a645-99c04834026f"
Host: api.spark.io
Content-Length: 2878
Expect: 100-continue
--135f5425-9342-4ffa-a645-99c04834026f
Content-Type: application/octet-stream
Content-Disposition: form-data; name=file; filename=file.ino; filename*=utf-8''file.ino
...
If you'll notice the difference in the Content-Type for the actual file being sent:
Successful: Content-Disposition: form-data; name="file"; filename="ms.ino"
Unsuccessful: Content-Disposition: form-data; name=file; filename=file.ino; filename*=utf-8''file.ino
Most specifically, the resolution was to add quotes around the name attribute.
Resolution:
formData.Add(fileContent, "\"file\"", "file.ino");
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?
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.