Equivalent of "curl -F" parameter for System.Net.Http.MultipartFormDataContent? - c#

I am trying to use the sonicAPI file/upload API in C#.
My attempt to translate the curl example to C# with HttpClient and MultipartFormDataContent returns the error 400 / Bad Request.
Content of the response :
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status code="400" />
<errors>
<error message="File upload failed: file is missing." parameter="file" error_code="10" />
</errors>
</response>
Example of the curl command-line shown in the documentation :
curl https://api.sonicapi.com/file/upload?access_id=$ACCESS_ID -Ffile=#Vocals.mp3
Code I've crafted so far :
public async Task<HttpResponseMessage> Post(string id, string fileName)
{
string url = string.Format("http://api.sonicapi.com/file/upload?access_id={0}", id);
var stream = new FileStream(fileName, FileMode.Open);
var client = new HttpClient { Timeout = TimeSpan.FromMinutes(10) };
var content = new MultipartFormDataContent();
content.Add(new StreamContent(stream), "file");
HttpResponseMessage message = await client.PostAsync(url, content);
string s = await message.Content.ReadAsStringAsync();
return message;
}
I've tried to remove "file" from content.Add(new StreamContent(stream), "file"); but it didn't help.
Note : the upload happens (i.e. it does not return immediately)
Do you know what is the equivalent of the curl -F parameter when using .NET web classes ?
EDIT :
Output of curl -v
* Hostname was NOT found in DNS cache
* Trying 87.106.252.119...
* Connected to api.sonicapi.com (87.106.252.119) port 80 (#0)
> POST /file/upload?access_id=xxxxxxxxxxxx HTTP/1.1
> User-Agent: curl/7.35.0
> Host: api.sonicapi.com
> Accept: */*
> Content-Length: 882266
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------b3c6dc0fc9
34fc71
>
< HTTP/1.1 100 Continue
< HTTP/1.1 201 Created
* Server nginx/0.7.67 is not blacklisted
< Server: nginx/0.7.67
< Date: Tue, 18 Feb 2014 21:14:09 GMT
< Content-Type: application/xml
< Connection: keep-alive
< X-Powered-By: Express
< X-Sonicapi-Request-Id: 6422cd9a-6069-4c2f-a3c5-0865c8ada6d5
< Access-Control-Allow-Origin: *
< location: /file/download?file_id=dae4e051-fe11-4058-a009-855dbb74de50
< X-Sonicapi-File-Id: dae4e051-fe11-4058-a009-855dbb74de50
< Content-Length: 249
<
<?xml version="1.0" encoding="utf-8"?><response><status code="201"/><file file_i
d="dae4e051-fe11-4058-a009-855dbb74de50" status="ready" href="/file/download?fil
e_id=dae4e051-fe11-4058-a009-855dbb74de50" remaining_lifetime_seconds="3599"/></
response>* Connection #0 to host api.sonicapi.com left intact
Output of the request using Fiddly :
POST http://api.sonicapi.com/file/upload?access_id=xxxxxxxx
HTTP/1.1
Content-Type: multipart/form-data; boundary="bd6fba7f-c173-4470-9c44-c9cc91f618a9"
Host: api.sonicapi.com
Content-Length: 882175
Expect: 100-continue
Connection: Keep-Alive
--bd6fba7f-c173-4470-9c44-c9cc91f618a9
Content-Disposition: form-data; name=file
RIFFvu
�WAVEfmt
(truncated)

Thanks to #bzlm, using Fiddler I managed to track what was missing :
Content disposition
Content type
And these needed to be set on streamContent rather than content.
public async Task<HttpResponseMessage> Post(string id, string fileName)
{
string url = string.Format("http://api.sonicapi.com/file/upload?access_id={0}", id);
var stream = new FileStream(fileName, FileMode.Open);
string name = Path.GetFileName(fileName);
var client = new HttpClient { Timeout = TimeSpan.FromMinutes(10) };
var streamContent = new StreamContent(stream);
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
streamContent.Headers.ContentDisposition.Name = "\"file\"";
streamContent.Headers.ContentDisposition.FileName = "\"" + name + "\"";
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
var content = new MultipartFormDataContent { streamContent };
HttpResponseMessage message = await client.PostAsync(url, content);
string s = await message.Content.ReadAsStringAsync();
return message;
}

Related

StatusCode: 404, Response when calling the Web API

I have a Web API which uploads the File content to the server.
[HttpPost]
[Route("SaveFileContent")]
public async Task<FileResponse> SaveFileContent([FromForm] SaveFileContentRequest request)
{
return await _service.SaveFile(request);
}
This is my call to the API:
public async Task<FileResponse> SaveFileContent(SaveFileContentRequest request)
{
try
{
var uri = "https://www.mycompanyurl.com";
using (var client = new HttpClient())
{
using (var form = new MultipartFormDataContent())
{
using (var fileContent = new ByteArrayContent(request.File))
{
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(fileContent, "file", request.FileName);
form.Add(new StringContent(request.MemberId), "MemberId);
form.Add(new StringContent(request.Country), "Country);
client.BaseAddress = new Uri(uri);
HttpResponseMessage response = await client.PostAsync("/api/Document/SaveFileContent", form);
FileResponse result = JsonConvert.DeserializeObject<FileResponse>(response.Content.ReadAsStringAsync().Result);
return result;
}
}
}
}
}
I get this response at PostAsync():
{StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Date: Sun, 21 Apr 2013 12:00:03 GMT
Server: Microsoft-HTTPAPI/2.0
Content-Length: 165
Content-Type: application/json; charset=utf-8
}}
When I try to run the API in my local - and use the localhost uri -
var uri = "http://localhost:51515";
It is working fine and getting the 200 OK response.
try to use the full route
[Route("~/api/Document/SaveFileContent")]
public async Task<FileResponse> SaveFileContent([FromForm] SaveFileContentRequest request)

convert curl command into c# code using httpclient

Command I need to convert :
curl.exe --digest -u login:pw -s -F "cert=#.\cert.pem" http://127.0.0.1/upload.htm
C# code I'm trying :
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("login", "pw");
HttpClient client = new HttpClient(handler);
client.BaseAddress = new Uri("http://127.0.0.1");
...
var content = new StringContent(fileContents);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "cert",
FileName = "cert.pem"
};
await client.PostAsync("/upload.htm", content);
Result :
<body><h1>HTTP/1.0 415 Unsupported Media Type</h1></body>
aldo tested the following c# code :
string executableLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
String path = Path.Combine(executableLocation, "cert.pem");
var fs = File.Open(path, FileMode.Open);
var multiPartContent = new MultipartFormDataContent();
var fc = new StreamContent(fs);
fc.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
multiPartContent.Add(fc, "certUsageUnspecified", "cert.pem");
var uploadCertificate = await client.PostAsync("/upload.htm", multiPartContent);
logger.Info(await uploadCertificate.Content.ReadAsStringAsync());
logger.Info("=== end upload certificate ===");
and result is the following :
<body><h1>HTTP/1.0 400 Bad Request</h1></body>
I don't know what I'm doing wrong there but I can't find the solution. It's working fine with the curl command.
curl -Fis for multipart -F, --form <name=content> Specify HTTP multipart POST data
Try the following:
var content = new MultipartFormDataContent();
var fs = File.Open(".\cert.pem", FileMode.Open);
var fc = new StreamContent(fs);
fc.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(fc, "cert", "cert.pem");
For information only
curl headers received with a file cert.pem whose content is "cert".
POST
Partial body: --------------------------87b709a918c0166a
Content-Disposition: form-data; name="cert"; filename="cert.pem"
Content-Type: application/octet-stream
"cert"
--------------------------87b709a918c0166a--
Body: --------------------------87b709a918c0166a
Content-Disposition: form-data; name="cert"; filename="cert.pem"
Content-Type: application/octet-stream
"cert"
--------------------------87b709a918c0166a--
The equivalent received with proposed .NET code:
POST
Partial body: --7ee9988d-bfd9-46d6-b0c9-74af30d7a6a2
Content-Disposition: form-data; name=cert; filename=cert.pem; filename*=utf-8''cert.pem
Content-Type: application/octet-stream
"cert"
--7ee9988d-bfd9-46d6-b0c9-74af30d7a6a2--
Body: --7ee9988d-bfd9-46d6-b0c9-74af30d7a6a2
Content-Disposition: form-data; name=cert; filename=cert.pem; filename*=utf-8''cert.pem
Content-Type: application/octet-stream
"cert"
--7ee9988d-bfd9-46d6-b0c9-74af30d7a6a2--
This is not strictly the same for Content-Disposition: header value but its very close.

Result from response.Content.ReadAsStringAsync() is empty string (Windows.Web.Http). How do I get response?

When I run this from a browser manually, it returns a response, that looks like this:
{"value":[{"ID":4}]}
Now, I am POSTing it from a Windows IoT Device, using the following code:
private async Task SendPostRequest(string username, string password, Dictionary<string, string> parameters)
{
try
{
// using Windows.Web.Http
HttpFormUrlEncodedContent formattedData = new HttpFormUrlEncodedContent(parameters);
using (HttpBaseProtocolFilter clientHandler = new HttpBaseProtocolFilter())
{
clientHandler.ServerCredential = GetCredentials(username, password);
using (HttpClient httpClient = new HttpClient(clientHandler))
{
//txtCommand.Text = "PostAsync";
HttpResponseMessage response = await httpClient.PostAsync(postUrl, formattedData);
txtResponse.Text = response.ToString();
response.EnsureSuccessStatusCode();
string responseString = await response.Content.ReadAsStringAsync();
txtResponse.Text += " responseString: " + responseString;
}
}
The result in responseString is an empty string.
The result of response.ToString() looks like this:
StatusCode: 200, ReasonPhrase: 'OK', Version: 2, Content: Windows.Web.Http.HttpStreamContent, Headers:
{
Persistent-Auth: true
Server: Microsoft-IIS/10.0
Transfer-Encoding: chunked
Date: Fri, 27 Aug 2021 15:16:38 GMT
X-Powered-By: ASP.NET
dbgate-version: 1.0
}{
Content-Type: text/plain
}

MultipartFormDataContent not adding Content-Type in HttpRequestBody

I have a simple HttpClient that does a multipart/form-data post to an endpoint.
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), url))
{
MultipartFormDataContent mfdc = new MultipartFormDataContent();
mfdc.Add(new StreamContent(content: new MemoryStream(Encoding.UTF8.GetBytes("This is from a file"))),
name: "file",
fileName: "File1.txt");
var response = await httpClient.PostAsync(url, mfdc);
}
}
This code produces this request:
Content-Type: multipart/form-data; boundary="0d411b9f-a36b-4326-97b6-85c781d348d2"
Content-Length: 196
Host: somehost.net
--0d411b9f-a36b-4326-97b6-85c781d348d2
Content-Disposition: form-data; name=file; filename=File1.txt; filename*=utf-8''File1.txt
This is from a file
--0d411b9f-a36b-4326-97b6-85c781d348d2--
The problem that I'm running into is that the API I'm calling is requiring the Content-Type in the request body as well.
The correct request should look like this:
Content-Type: multipart/form-data; boundary="0d411b9f-a36b-4326-97b6-85c781d348d2"
Content-Length: 196
Host: somehost.net
--0d411b9f-a36b-4326-97b6-85c781d348d2
Content-Disposition: form-data; name=file; filename=File1.txt; filename*=utf-8''File1.txt
Content-Type: text/plain
This is from a file
--0d411b9f-a36b-4326-97b6-85c781d348d2--
You have not added the content type of the content to be sent. So it will not be included in the request
using (var httpClient = new HttpClient()) {
MultipartFormDataContent form = new MultipartFormDataContent();
var stream = new MemoryStream(Encoding.UTF8.GetBytes("This is from a file"));
var fileContent = new StreamContent(content: stream);
//setting content type
fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
form.Add(fileContent, name: "file", fileName: "File1.txt");
var response = await httpClient.PostAsync(url, form);
}

Convert CURL to C# using HttpClient

I am trying to convert the following CURL command:
curl --request POST --url http://localhost:8042/tools/find --data "{\"Level\":\"Study\",\"Query\":{\"Modality\":\"MR\",\"StudyInstanceUID\":\"*\"}}"
to a C# rest client method, using HttpClient without success.
One approach i have been trying is the following:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:8042");
string test = "{\"Level\":\"Study\",\"Query\":{\"Modality\":\"MR\",\"StudyInstanceUID\":\"*\"}}";
var requestContent = new StringContent(test, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await client.PostAsync("tools/find", requestContent);
HttpContent responseContent = response.Content;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
// Write the output.
Console.WriteLine(await reader.ReadToEndAsync());
return await reader.ReadToEndAsync();
}
}
But either i am getting some timeout exceptions or the returned result in NULL.
This is what i am getting when i run the CURL command:
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying ::1...
* TCP_NODELAY set
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8042 (#0)
> POST /tools/find HTTP/1.1
> Host: localhost:8042
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 66
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 66 out of 66 bytes
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: application/json; charset=utf-8
< Content-Length: 1023
<
[
"06a5bb05-acf327ff-1b1f7432-543a3572-d0778630",
"0d2858ee-9bb9557f-6779c861-4e55604a-bbd9d561",
"241d9063-668f6d5a-84d5e791-aae25988-cbe330e4",
"2a47771d-a7fed498-2ea74733-6ba5b408-0af517d0",
"2c22461c-2529103d-1c3bbf0e-5c1011b7-ef4c4702",
"2e89270f-77b2368b-ec8f7a47-48922528-6d82a563",
"46151c0f-a92e4ffe-b3964a0b-0a217ff0-e138a9b0",
"4ac07d24-df6720d8-410ded38-80c42f81-029b826d",
"6ae5803f-564d02d6-ce2d03c9-87029ebb-c5f5b783",
"6c4dd689-5dc50cc7-7c0b07e1-231c8f06-10a50343",
"79a0e646-d244dced-2a2ac6d0-e61e6029-38b1e61e",
"7beb9698-c3e13f1a-5449e8c0-06f61be7-0285b222",
"8c35bbfa-1f00d0bb-50fdddea-c8b8f085-20ef243a",
"9a318c16-75a5cae8-3f42dd60-2ab5d0c0-664e78d1",
"a7b43909-4ecfe2fe-12f414ad-dc1d013e-0665e60b",
"b85380e8-e66db7da-d575e3d3-80bce548-71d5c251",
"d07b73c3-77cad4ff-1bf045d9-d8677f33-cd4c79f5",
"ec81b8b0-c6f95b97-2a2299ca-fa4d3f68-d79f0079",
"ec9c971a-e66c350c-f808f182-7716b99c-4c8f6f86",
"efe910b9-3bb0e298-c9ec181b-3985c6be-a6b74a89"
]
* Connection #0 to host localhost left intact
Any ideas about how can i convert this command to working c# code?
don't repeat the full URL in PostAsync if you are setting BaseAddress
using (var client = new HttpClient()){
client.BaseAddress = new Uri("http://localhost:8042");
...
var response = await client.PostAsync("tools/find", requestContent);
...
}
After some research and testing here is the code to make a corresponding to curl:
curl --request POST --url http://localhost:8042/tools/find --data "{\"Level\":\"Study\",\"Query\":{\"Modality\":\"MR\",\"StudyInstanceUID\":\"*\"}}"
post request in C#:
public async System.Threading.Tasks.Task<string> MyMethod(string studyInstanceUID)
{
using (var client = new HttpClient())
{
string data = "{\"Level\":\"Study\",\"Query\":{\"Modality\":\"MR\",\"StudyInstanceUID\":\"" + studyInstanceUID + "\"}}";
var requestContent = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
client.DefaultRequestHeaders.Add("User-Agent", "Anything");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (reqAuth)
{
var byteArray = Encoding.ASCII.GetBytes(authString);
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
}
HttpResponseMessage response = await client.PostAsync(baseUrl + "tools/find", requestContent);
var responseContent = response.Content;
return responseContent.ReadAsStringAsync().Result;
}
}
If you don't mind using a small wrapper library, Flurl (disclaimer: I'm the author), which wraps HttpClient and Json.NET, makes this about as simple as using cURL.
using Flurl.Http;
var results = await "http://localhost:8042/tools/find"
.PostJsonAsync(new { Level = "Study", Query = new { Modality = "MR", StudyInstanceUID = "*" }})
.ReceiveJson<string[]>();
Note that JSON serializaiton is handled implicitly, so you're only ever dealing with strongly typed C# objects, rather than stringifying/parsing the JSON that goes in and comes out.

Categories

Resources