Upload file via POST call with C#, RestSharp, Redmine API - c#

I'm developing a C# app that uses Redmine REST API, it uses RestSharp Client. All other REST calls I make work fine but I cannot find a way to upload attachments. I've widely searched the web and tried many solutions but nothing worked.
Redmine documentiation on attachments: http://www.redmine.org/projects/redmine/wiki/Rest_api#Attaching-files
The code actually looks like:
RestClient client = new RestClient("http://myclient/redmine/");
client.Authenticator = new HttpBasicAuthenticator("myuser", "mypsw");
var request2 = new RestRequest("uploads.json", Method.POST);
request2.AddHeader("Content-Type", "application/octet-stream");
request2.RequestFormat = RestSharp.DataFormat.Json;
byte[] dataToSend = File.ReadAllBytes(AddIssue.attach.Text);
request2.AddBody(dataToSend);
IRestResponse response2 = client.Execute(request2);
resultbox.Text = response2.Content;
If I execute it above nothing happens and the response is empty. If I remove line 7 (the AddBody), it actually works but of course nothing is uploaded, JSON response:
{
"upload": {
"token": "11."
}
}
So actually, the real question is what to punt in AddBody() to send the file as application/octet-stream. Since RestSharp also has a request.AddFile() method, I tried it too in different ways but nothing...
Any help much appreciated!

As I mentioned in my comment, it sounds like Redmine might have requirements similar to Dropbox. Here is the solution that worked for me (based on the question Upload to dropbox using Restsharp PCL):
public static void UploadFileToDropbox(string filePath)
{
RestClient client = new RestClient("https://api-content.dropbox.com/1/");
IRestRequest request = new RestRequest("files_put/auto/{path}", Method.PUT);
FileInfo fileInfo = new FileInfo(filePath);
long fileLength = fileInfo.Length;
request.AddHeader("Authorization", "Bearer INSERT_DEVELOPER_TOKEN_HERE");
request.AddHeader("Content-Length", fileLength.ToString());
request.AddUrlSegment("path", string.Format("Public/{0}", fileInfo.Name));
byte[] data = File.ReadAllBytes(filePath);
var body = new Parameter
{
Name = "file",
Value = data,
Type = ParameterType.RequestBody,
};
request.Parameters.Add(body);
IRestResponse response = client.Execute(request);
}
Also published as a Gist.
I know this isn't your exact situation, but hopefully it gives you some ideas.

Related

Why do I keep getting "Missing/Malformed URL Parameters" when consuming my API in C# (RestSharp) with VS 2022?

I'm trying to do a POST request in C#, using RestSharp, in Visual Studio 2022.
I tested the API using Postman, which was successful. The API requires a 64-bit encoded Basic Auth (which I have), and a unique API key (which I also have). I then looked at the C# Restsharp code provided by Postman, and tried to copy paste it into my VS project, but most of the libraries were deprecated. I then ended up modifying the code so it didn't give me any errors, and the code itself runs, but getting a semantic error: the request returns "Missing or Malformed URL parameters". In the body parameter, I send one parameter in the form
var body = #"{""fieldNameHere"": intHere}";
My full code (redacted):
var options = new RestClientOptions("URLHERE")
{
Timeout = -1
};
var client = new RestClient(options);
var request = new RestRequest
{
Method = Method.Post
};
request.AddHeader("API_KEY", "keyHere");
request.AddHeader("Authorization", "authHere");
request.AddHeader("Content-Type", "application/json");
var body = #"{""fieldNameHere"": intHere}";
request.AddParameter("application/json; charset=utf-8", body, ParameterType.RequestBody);
request.RequestFormat = DataFormat.Json;
RestResponse response = await client.ExecuteAsync(request);
So I tried using a JObject for the parameter, got the same error, this is my code:
JObject jObjectBody = new JObject();
jObjectBody.Add("FieldName", intHere);
request.AddParameter("application/json", jObjectBody, ParameterType.RequestBody);
var clientValue = await client.ExecuteAsync(request);
I also tried using HttpWebRequest, but got an auth error so not sure what was going on there, didn't get that anywhere else. Would prefer to use RestClient anyway. This is the other way I tried to do the body parameter:
string postData = "{\"FieldNameHere\":" + intHere + "}";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
I haven't found anything that works yet. If someone can guide me towards a solution I'd be mad grateful, thanks. It definitely seems to be the body giving me the issue.
Ah! For some reason, phrasing my body like this worked:
var body = new { FieldNameHere = intHere }
request.AddJsonBody(body);
I have no idea why this worked, but it did!
A list of things that DID NOT work:
JObject technique
switching header placement
encoding the auth myself
how the RR instantiation is set up
with and without explicitly saying application/json (also including the charset and excluding)
using HttpWebRequest instead of RestSharp

Translating RestSharp request to HttpClient request

I'm programmatically uploading files to a remote server. The files are moderately large and I'd like to present a progress report to my users so they can see something happening. I was able to implement the upload using Postman which helpfully translated the whole thing to RestSharp.
But RestSharp does not provide any kind of progress tracking. I tried to implement the same functionality using HttpClient but it goes wrong somewhere the and server just throws a "400 - Bad Request" without telling exactly what is bad about it (its API documentation is also not for the faint of heart).
So, here's what Postman / RestSharp provide and which is working:
var client = new RestClient("https://opencast/ingest/addMediaPackage");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Basic FooBarBaz=");
request.AddParameter("creator", file.Creator);
request.AddParameter("title", file.Title);
request.AddParameter("flavor", "presentation/source");
request.AddParameter("description", file.Description);
try
{
request.AddFile("BODY", path);
IRestResponse response = await client.ExecuteAsync(request);
_logger.LogInformation($"Response after file upload: {response.StatusCode}");
File.Delete(path);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception when uploading files: {Message}", ex.Message);
}
and here's what I tried to do with HttpClient (without try-catch):
var request = new HttpRequestMessage(HttpMethod.Post, $"https://opencast/ingest/addMediaPackage");
request.Headers.Add("Authorization", "Basic FooBarBaz=");
using var form = new MultipartFormDataContent();
using var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(path));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(fileContent, "BODY", Path.GetFileName(path));
form.Add(new StringContent(file.Creator), "creator");
form.Add(new StringContent(file.Title), "title");
form.Add(new StringContent("presentation/source"), "flavor");
form.Add(new StringContent(file.Description), "description");
request.Content = form;
var client = clientFactory.CreateClient(); //which is a IHttpClientFactory
var response = await client.SendAsync(request);
This code sends the file to the server which, after completing the upload, throws a 400.
Currently not seeing the difference. I could intercept the requests to see where they differ but maybe someone here can see the problem right away?
Update: It gets weirder. If I just use clientFactory.PostAsync(form) and add the Auth headers through form.Add then I get a 200 (i.e. Success) but the server simply swallows the file.
Okay, I found the solution. I'm not sure whether the WTF is me or the guys behind the server but...
... you need to add the fileContent last.
Yes, the order of the parameters matters for this.

Mailgun sending attachment with RestSharp

I'm using RestSharp to try and send an attachment with the Mailgun API. I have tried attaching from both a file in the system using a hardcoded path and also from a binary file stored in the database using ToArray() method on the varbinary(MAX) (SQL Server) property both with no success.
The attachment technically sends, but when the email arrives in my inbox the file size is always roughly 302bytes big and is always corrupt. I have tried 3 different files and get the same problem each time.
The rest of the email sends, delivers and displays fine. It's just the attachments that are broken.
Breakdown of code:
// Doesnt work(Data property is varbinary(MAX)
request.AddFileBytes("attachment",databaseModel.Data.ToArray(),databaseModel.Filename, "multipart/form-data");
// Also doesnt work(Data property is varbinary(MAX)
request.AddFile("attachment",databaseModel.Data.ToArray(),databaseModel.Filename, "multipart/form-data");
// Also doesnt work
var path = #"D:\Template.pdf";
request.AddFile("attachment",path,"multipart/form-data");
This code works:
public static void Main(string[] args)
{
Console.WriteLine(SendSimpleMessage().Content.ToString());
Console.ReadLine();
}
public static IRestResponse SendSimpleMessage()
{
var path1 = #"C:\Users\User\Pictures\website preview";
var fileName = "Learn.png";
RestClient client = new RestClient();
client.BaseUrl = new Uri("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-934345306fead7de0296ec2fb96a143");
RestRequest request = new RestRequest();
request.AddParameter("domain", "mydomain.info", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <example#mydomain.info>");
request.AddParameter("to", "peter.cech#gmail.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness! This is all about the text only. Just testing the text of this email.";
request.AddFile("attachment", Path.Combine(path1,fileName));
request.Method = Method.POST;
return client.Execute(request);
}
I figured it out..
Not supposed to add "multipart/form-data" on the request.AddFile();
Removing this fixes the problem.

Posting multiple binary files in a single POST with httpclient?

How would one post multiple binaries in a single http POST operation using C# httpclient? I can't seem to find information on how to deal with httpcontent in this way - just doing a postASync with stream data twice?
Dug around a bit more and experimented, and finally found what seems like a working solution. I tried this on a test server with some images on HD - both sent, both worked. With two stream examples.
var client = new HttpClient();
var stream3 = new FileStream("saved.jpg", FileMode.Open);
var stream2 = new FileStream("saved2.jpg", FileMode.Open);
var dic = new Dictionary<string, string>();
dic.Add("Test1", "This was the first test.");
var addy = "http://posttestserver.com/post.php";
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(stream2), "s1", "Saved1.jpg");
content.Add(new StreamContent(stream3), "s2", "Saved2.jpg");
var response = await client.PostAsync(addy, content);
response.EnsureSuccessStatusCode();
string finalresults = await response.Content.ReadAsStringAsync();
}
It will depend on the implementation of the API that you are sending your files to but typically if multiple files are sent in a single POST request then it is sent as multipart/form-data. Have a look at this post for sending multipart/form-data through HttpClient.

Unable to create a shared link using the Box API V2

UPDATE: I figured it out and posted the answer below.
All I'm trying to do is update any file attribute. Description, name, anything, but no matter how I format it I get a 403.
I need to be able to modify a file so it can be shared via the Box API from a cloud app. I'm updating someone else's code from V1, but they are no longer available... I've tried many things but mostly just get 403 Forbidden errors.
There are no issues with OAuth2, that works fine and I can list files and folders, but can not modify them. This question is about sharing, but I can't change a description either. The box account is mine and I authenticate with my admin credentials. Any suggestions would be appreciated.
Here is the method I am using. I pass in the fileId and token and I've left out try/catch etc. for brevity.
string uri = string.Format("https://api.box.com/2.0/files/{0}", fileId);
string body = "{\"shared_link\": {\"access\": \"open\"}}";
byte[] postArray = Encoding.ASCII.GetBytes(body);
using (var client = new WebClient())
{
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
client.Headers.Add("Authorization: Bearer " + token);
var response = client.UploadData(uri, postArray);
var responseString = Encoding.Default.GetString(response);
}
Thanks.
Okay, My Homer Simpson moment...
UploadData is a POST, I needed to do a PUT. Here is the solution.
string uri = String.Format(UriFiles, fileId);
string response = string.Empty;
string body = "{\"shared_link\": {\"access\": \"open\"}}";
byte[] postArray = Encoding.ASCII.GetBytes(body);
try
{
using (var client = new WebClient())
{
client.Headers.Add("Authorization: Bearer " + token);
client.Headers.Add("Content-Type", "application/json");
response = client.UploadString(uri, "PUT", body);
}
}
catch (Exception ex)
{
return null;
}
return response;
try changing your content type to 'multipart/form-data'?
I just looked up the api at: https://developers.box.com/docs/#files-upload-a-file
and it looks like the server is expecting a multipart post
here is stack overflow post on posting multipart data:
ASP.NET WebApi: how to perform a multipart post with file upload using WebApi HttpClient

Categories