Httpclient multipart/form-data post image and json same time - c#

I'm trying to upload image and json in one request using c# code, but server always returns 400- bad request. Executing same request using fiddler returns status code 200. help...
Here is my fiddler code :
------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="application/json" Content-Type: application/json
{"type": "Personal","comments": ["Lorem", "Ipsum" ] }
------WebKitFormBoundary7MA4YWxkTrZu0gW-- Content-Disposition: form-data; name="fieldNameHere"; filename="1111.jpg"
Content-Type: image/jpeg
<#INCLUDE C:\Users\user\Desktop\New folder\1111.jpg#>
And implementation in c#:
var boundary = "Upload----" + DateTime.Now.Ticks.ToString();
MultipartFormDataContent form = new MultipartFormDataContent(boundary);
StringContent content = new StringContent(bodyJson);
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
form.Add(content, "application/json");
var imageContent = new ByteArrayContent(image);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");
form.Add(imageContent, "image/jpeg", "image.jpg");
var responseTask = _httpClient.PostAsync(url, form).Result;
the response is always same :

You can pass the parameter as a string content , check the below sample.
public async Task<JObject> ExecutePostAsync(Stream myStreem, string url, string token, string parameter1, string parameter2, string parameter3)
{
try
{
using (var content = new MultipartFormDataContent("----MyBoundary"))
{
using (var memoryStream = myStreem)
{
using (var stream = new StreamContent(memoryStream))
{
content.Add(stream, "file", Guid.NewGuid().ToString() + ".jpg");
content.Add(new StringContent(parameter1), "parameter1");
content.Add(new StringContent(parameter3), "parameter2");
content.Add(new StringContent(parameter3), "parameter3");
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var responce = await client.PostAsync(url, content);
string contents = await responce.Content.ReadAsStringAsync();
return (JObject.Parse(contents));
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
In API get the data from FORM request
public async Task<IHttpActionResult> UploadFile()
{
string parameter1 = HttpContext.Current.Request.Form["parameter1"];
string parameter2 = HttpContext.Current.Request.Form["parameter2"];
string parameter3 = HttpContext.Current.Request.Form["parameter3"];
}

Related

Error Creating Folder in Sharepoint using REST API C#

I can accomplish all other tasks with the rest API, like uploading and downloading files, navigating through the file directory. I just keep getting either 400 Bad Request or sometimes with some tries I'll get 500 Internal Server Error. Also, I can create the request on postman and its successful
this is what the request should look like the rest is me creating it in c#
POST https://{site_url}/_api/web/folders
Authorization: "Bearer " + accessToken
Accept: "application/json;odata=verbose"
Content-Type: "application/json"
Content-Length: {length of request body as integer}
X-RequestDigest: "{form_digest_value}"
{
"__metadata": {
"type": "SP.Folder"
},
"ServerRelativeUrl": "/document library relative url/folder name"
}
private async Task PostFolderSharePoint(string url, string serverRelativeUrl)
{
string accessToken = GetAccessToken().GetAwaiter().GetResult();
string jsoncontent = JsonConvert.SerializeObject("{\"__metadata\": {\"type\": \"SP.Folder\"},\"ServerRelativeUrl\": serverRelativeUrl}");
var content = new StringContent(jsoncontent, Encoding.UTF8, "application/json");
var FormDiGestValue = await GetFormDigestValue(accessToken);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var requestMessage = new HttpRequestMessage(HttpMethod.Post,url) { Content = content };
requestMessage.Headers.Add("X-RequestDigest", FormDiGestValue);
HttpResponseMessage response = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
return response;
}
This is how I create a folder with the Sharepoint REST API:
public async Task<string> CreateFolder(string folderName, string relativeUrl)
{
try
{
var url = "https://your.sharepoint.com/sites/devsite/_api/web/folders";
var json = "{\"ServerRelativeUrl\": \"" + relativeUrl + "/" + folderName + "\"}";
var payload = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-User-Agent", "spoc");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var response = await client.PostAsync(url, payload);
return await response.Content.ReadAsStringAsync();
}
catch (WebException we)
{
throw new SomethingException(we);
}
}
and to use it:
var modFolder = await spocRest.CreateFolder("MOD1", "Shared Documents");

Trouble uploading a document to Salesforce using c#

I'm referencing the url https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm#inserting_a_contentversion and I see the curl methods, but c# can't use c#. So I've attempted creating the multiPartForm request with no avail.
Below are the two specific curl calls I'm attempting to recreate in C#.
--boundary_string
Content-Disposition: form-data; name="entity_content";
Content-Type: application/json
{
"ContentDocumentId" : "069D00000000so2",
"ReasonForChange" : "Marketing materials updated",
"PathOnClient" : "Q1 Sales Brochure.pdf"
}
--boundary_string
Content-Type: application/octet-stream
Content-Disposition: form-data; name="VersionData"; filename="Q1 Sales Brochure.pdf"
Binary data goes here.
--boundary_string--
and
--boundary_string
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json
{
"Description" : "Marketing brochure for Q1 2011",
"Keywords" : "marketing,sales,update",
"FolderId" : "005D0000001GiU7",
"Name" : "Marketing Brochure Q1",
"Type" : "pdf"
}
--boundary_string
Content-Type: application/pdf
Content-Disposition: form-data; name="Body"; filename="2011Q1MktgBrochure.pdf"
Binary data goes here.
--boundary_string--
Here are my code attempts, with the byte[] and in some case a base64 string. I've switched out the base[] and the base64 string in each case and I have had no luck, the json string is correctly formatted, the token is valid, just keep getting back a 400 error. (bad request) from salesforce.
public async Task<HttpResponseMessage> PostBlobAsync(string url, string token, string json, byte[] pdfBase64)
{
try
{
using (var httpContent = new MultipartFormDataContent("boundary_string"))
{
httpContent.Headers.ContentType.MediaType = "multipart/form-data";
httpContent.Add(new StringContent(json, System.Text.Encoding.UTF8, "application/json"));
//var content = new StringContent(pdfBase64.ToString(), System.Text.Encoding.UTF8, "application/octet-stream");
var content = new ByteArrayContent(pdfBase64, 0, pdfBase64.Length - 1);
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
_logger.LogInformation("HTTP Client PostAsync calling URL:{0} at {1}", url, DateTime.UtcNow);
_httpClient.UseBearerToken(token);
var response = await _httpClient.PostAsync(url, httpContent);
if (!response.IsSuccessStatusCode)
{
var ex = CreateException(response);
throw ex;
}
return response;
}
}
catch (Exception ex)
{
_logger.LogInformation(ex.Message, url, DateTime.UtcNow);
throw ex;
}
}
public async Task<HttpResponseMessage> PostBlobAsync2(string url, string token, string json, string fileBytes)
{
try
{
using (var httpContent = new MultipartFormDataContent("boundary_string"))
{
httpContent.Headers.ContentType.MediaType = "multipart/form-data";
var content1 = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
var contentDisposition1 = new ContentDispositionHeaderValue("form-data");
contentDisposition1.Name = "entity_content";
content1.Headers.ContentDisposition = contentDisposition1;
httpContent.Add(content1);
//var content2 = new StringContent(pdfBase64.ToString(), System.Text.Encoding.UTF8, "application/octet-stream");
//var content2 = new StringContent(fileBytes, System.Text.Encoding.UTF8, "application/pdf");
var content2 = new StringContent(fileBytes);
content2.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
var contentDisposition2 = new ContentDispositionHeaderValue("form-data");
contentDisposition2.Name = "VersionData";
contentDisposition2.FileName = "Change Request.pdf";
httpContent.Add(content2);
_logger.LogInformation("HTTP Client PostAsync calling URL:{0} at {1}", url, DateTime.UtcNow);
_httpClient.UseBearerToken(token);
var response = await _httpClient.PostAsync(url, httpContent);
if (!response.IsSuccessStatusCode)
{
var ex = CreateException(response);
throw ex;
}
return response;
}
}
catch (Exception ex)
{
_logger.LogInformation(ex.Message, url, DateTime.UtcNow);
throw ex;
}
}
public async Task<HttpResponseMessage> PostBlobAsync3(string url, string token, string caseId, string fileBytes)
{
try
{
using (var httpContent = new MultipartFormDataContent("boundary_string"))
{
//httpContent.Headers.ContentType.MediaType = "multipart/form-data";
var dictionary = new Dictionary<string, string>();
dictionary.Add("Description", "New Change Request");
dictionary.Add("Keywords", "Change,Request");
dictionary.Add("FolderId", caseId);
dictionary.Add("Type", "pdf");
var json = JsonConvert.SerializeObject(dictionary);
var content1 = new StringContent(json);
content1.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
var contentDisposition1 = new ContentDispositionHeaderValue("form-data");
contentDisposition1.Name = "entity_document";
content1.Headers.ContentDisposition = contentDisposition1;
httpContent.Add(content1);
//var content2 = new StringContent(pdfBase64.ToString(), System.Text.Encoding.UTF8, "application/octet-stream");
//var content2 = new StringContent(fileBytes, System.Text.Encoding.UTF8, "application/pdf");
var content2 = new StringContent(fileBytes);
content2.Headers.ContentType = MediaTypeHeaderValue.Parse("application/pdf");
var contentDisposition2 = new ContentDispositionHeaderValue("form-data");
contentDisposition2.Name = "Body";
contentDisposition2.FileName = "Change Request.pdf";
httpContent.Add(content2);
_logger.LogInformation("HTTP Client PostAsync calling URL:{0} at {1}", url, DateTime.UtcNow);
_httpClient.UseBearerToken(token);
var response = await _httpClient.PostAsync(url, httpContent);
if (!response.IsSuccessStatusCode)
{
var ex = CreateException(response);
throw ex;
}
return response;
}
}
catch (Exception ex)
{
_logger.LogInformation(ex.Message, url, DateTime.UtcNow);
throw ex;
}
}
public async Task<bool> PostBlobAsync4(string url, string token, string caseId, byte[] fileBytes)
{
string boundary = "boundary_string";
var req = System.Net.WebRequest.Create(url);
req.Headers.Add("Authorization: OAuth " + token);
req.ContentType = "multipart/form-data; boundary=" + boundary;
req.Method = "POST";
var os = req.GetRequestStream();
string body = "";
body += $"\r\n--{boundary}\r\n";
body += "Content-Disposition: form-data; name='entity_document'\r\n";
body += "Content-Type: application/json\r\n\r\n";
var dictionary = new Dictionary<string, string>();
dictionary.Add("Description", "New Change Request");
dictionary.Add("Keywords", "Change,Request");
dictionary.Add("FolderId", caseId);
dictionary.Add("Type", "pdf");
dictionary.Add("Name", "Change Request");
var json = JsonConvert.SerializeObject(dictionary);
body += json;
// Add header for binary part
body += "\r\n--" + boundary + "\r\n";
body += "Content-Disposition: form-data; name='Body'; filename='1.txt'\r\n";
body += "Content-Type: binary/octet-stream\r\n\r\n";
byte[] data = System.Text.Encoding.ASCII.GetBytes(body);
os.Write(data, 0, data.Length);
os.Write(fileBytes, 0, fileBytes.Length);
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
os.Write(trailer, 0, trailer.Length);
os.Close();
WebResponse resp;
try
{
resp = await req.GetResponseAsync();
}
catch(WebException ex)
{
return false;
}
return true;
}

Send IFileForm to API with JSON

I'm receiving a IFormFile object from an API POST request, and I'm trying to pass that file through to another API endpoint via a POST request with some JSON attached.
I am able to embed the image inside the request, but the server I'm sending to always returns a 500. I believe the issue the fact that I'm sending the JSON and the IFormFile together, as when I send one or the other it works okay (albeit, I'm not receiving the 200 response I want due to one or the other being missing).
Here is the code I'm using to post the data:
public async Task<APIResponse<T>> PostForm<T>(string baseURL, string apiName, IFormFile file, string jsonToPost = "", Dictionary<string, string> headers = null) where T : class
{
using (var client = new HttpClient() { BaseAddress = new Uri(baseURL) })
{
client.BaseAddress = new Uri(baseURL);
HttpResponseMessage response = null;
if (headers != null)
{
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
try
{
var multiContent = new MultipartFormDataContent();
var fileStreamContent = new StreamContent(file.OpenReadStream());
multiContent.Add(fileStreamContent, "file", file.FileName);
multiContent.Add(new StringContent(jsonToPost,System.Text.Encoding.UTF8, "application/json"));
response = await client.PostAsync(apiName, multiContent);
return new APIResponse<T>(JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync()), null, (int)response.StatusCode);
}
catch
{
return new APIResponse<T>(null, await response.Content.ReadAsStringAsync(), (int)response.StatusCode);
}
}
}
And here is what the JSON looks like if it helps anyone:
{
"orderId": 694532,
"type": "4x6",
"copies": 1,
"sizing": "Crop",
"priceToUser": 4000,
"md5hash": "5fed252e505f8542b38d9c0b1f221a71"
}
Edit - Updated code:
public async Task<APIResponse<T>> PostForm<T>(string baseURL, string apiName, IFormFile file, string jsonToPost = "", Dictionary<string, string> headers = null) where T : class
{
var client = new HttpClient();
client.BaseAddress = new Uri(baseURL);
HttpResponseMessage response = null;
if (headers != null)
{
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
try
{
var multiContent = new MultipartFormDataContent();
multiContent.Add(new StringContent(jsonToPost,System.Text.Encoding.UTF8, "application/json"));
multiContent.Add(GetByteArray(file), "file", file.FileName);
response = await client.PostAsync(apiName, multiContent);
return new APIResponse<T>(JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync()), null, (int)response.StatusCode);
}
catch
{
return new APIResponse<T>(null, await response.Content.ReadAsStringAsync(), (int)response.StatusCode);
}
}
private ByteArrayContent GetByteArray(IFormFile file){
byte[] data;
var br = new BinaryReader(file.OpenReadStream());
data = br.ReadBytes((int)file.OpenReadStream().Length);
return new ByteArrayContent(data);
}

Error while trying to upload files to Zendesk Api 2 in c#

I am trying to upload an image using Zendesk API v2, I am posting the file to /api/v2/uploads.json using RestSharp, and the file appears in the ticket once I create the ticket and add the attachment, the issue is that if I upload an image it won't open on the ticket, it is broken, if its a .txt file it has extra data there, this is my method:
var client = new RestSharp.RestClient(model.RequestUri);
client.Authenticator = new HttpBasicAuthenticator(string.Format("{0}/token", model.Username), model.Password);
var request = new RestRequest("/api/v2/uploads.json", Method.POST);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "text/plain");
request.AlwaysMultipartFormData = true;
request.Parameters.Clear();
request.RequestFormat = RestSharp.DataFormat.Json;
//request.AddBody(createUpload);
byte[] bytes = System.IO.File.ReadAllBytes(HttpContext.Current.Server.MapPath("~/Media/uploads/test.txt"));
request.AddFileBytes("Attachment", bytes, "test.txt", contentType: "text/plain");
request.AddParameter("filename", "test.txt");
IRestResponse response = client.Execute(request);
var content = JObject.Parse(response.Content);
return content["upload"]["token"].ToString();
This is the resulting txt file that's attached to the ticket:
-------------------------------28947758029299
Content-Disposition: form-data; name="filename"
test.txt
-------------------------------28947758029299
Content-Disposition: form-data; name="Attachment"; filename="test.txt"
Content-Type: application/octet-stream
testing txt
-------------------------------28947758029299--
The original file just has:
testing txt
Any ideas of what the error could be?
Thanks.
I solved the issue using an external library called ZendeskApi that's recommended in the Zendesk documentation: https://github.com/justeat/ZendeskApiClient
By using this library I was able to upload the attachments successfully and it works with any kind of file as well. It is also very easy to use, my method looks like this now:
IZendeskClient client = new ZendeskClient(
new Uri(model.RequestUri),
new ZendeskDefaultConfiguration(model.Username,
model.Password)
);
UploadRequest request = new UploadRequest() {
Item = model.Attachment.ConvertToZendeskFile()
};
IResponse<Upload> response = client.Upload.Post(request);
return response.Item.Token;
This is the ConvertToZendeskFile method:
private static ZendeskFile ConvertToZendeskFile(this HttpPostedFileBase rawFile)
{
return new ZendeskFile()
{
FileName = rawFile.FileName,
ContentType = rawFile.ContentType,
InputStream = rawFile.InputStream
};
}
The last step was creating a class that implemented IHttpPostedFile from the API:
public class ZendeskFile : IHttpPostedFile
{
public string ContentType { get; set; }
public string FileName { get; set; }
public Stream InputStream { get; set; }
}
This solved the issue for me, I hope it can help anyone facing the same problem.
I've managed to upload images and PDFs to Zendesk using a code snippet similar to this:
var client = new RestClient(apiUrl);
client.Authenticator = new HttpBasicAuthenticator(username + "/token", token);
client.AddDefaultHeader("Accept", "application/json");
string name = "name";
byte[] data; //Read all bytes of file
string filename = "filename.jpg";
var request = new RestRequest("/uploads.json", Method.POST);
request.AddFile(name, data, filename, "application/binary");
request.AddQueryParameter("filename", filename);
var response = client.Execute(request);
Need to add header ContentType=application/binary and provide file name in the URI ?filename=myfile.dat:
HttpClient client = [...];
var content = new ByteArrayContent(fileByteArray);
content.Headers.ContentType = new MediaTypeHeaderValue("application/binary");
HttpResponseMessage response = await client.PostAsync(url, content);
string responseString = await response.Content.ReadAsStringAsync();
From Zendesk documentation:
curl "https://{subdomain}.zendesk.com/api/v2/uploads.json?filename=myfile.dat&token={optional_token}" \
-v -u {email_address}:{password} \
-H "Content-Type: application/binary" \
--data-binary #file.dat -X POST
I had the same problem, Restsharp was sending the file as multipart, the only solution that worked for me was to send the file as parameter with content "application/binary".
public string UploadFile(ZendeskFile file)
{
try
{
var request = new RestRequest(FileUploadsPath, Method.POST);
request.AddQueryParameter("filename", file.Name);
request.AddParameter("application/binary", file.Data, ParameterType.RequestBody);
var response = Execute<UploadZendeskFileResponse>(request);
var result = JsonConvert.DeserializeObject<UploadZendeskFileResponse>(response.Content);
return result.upload.token;
}
catch (Exception ex)
{
throw ex;
}
}
I hope this helps someone else.
In my case, I did something like this. Hope you won't waste 6 hours like me!
public async Task UploadImageToZendesk(IFormFile image)
{
byte[] fileByteArray;
var request = new HttpRequestMessage();
var client = new HttpClient();
await using (var fileStream = image.OpenReadStream())
await using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream);
fileByteArray = memoryStream.ToArray();
}
ByteArrayContent byteContent = new ByteArrayContent(fileByteArray);
request.Content = byteContent;
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse($"application/binary");
await client.SendAsync(request);
}

Download file prompt when using WebAPI HttpResponseMessage

I have a method in my API that returns a HttpResponseMessage:
[HttpGet, HoodPeekAuthFilter]
public HttpResponseMessage GlobalOverview()
{
try
{
StatsRepo _statsRepo = new StatsRepo();
string file = _statsRepo.IncidentData().AsCSVString();
if (file == null)
{
return Request.CreateResponse(HttpStatusCode.NoContent);
}
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StringContent(file);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "GlobalOverview.csv";
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
In my MVC Web Apllication i have a controller Action that needs to call the API and return the file:
[Authorize]
[HttpGet]
public HttpResponseMessage GlobalOverview()
{
HttpResponseMessage file = new HttpResponseMessage();
using (HttpClient httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(this.UserName + ':' + this.Password)));
Task<HttpResponseMessage> response = httpClient.GetAsync("api.someDomain/Reporting/GlobalOverview");
file = response.Result;
}
return file;
}
If i access the API url directly it prompts me for a username and password, and then the Save File dialogue appears, and i can download the file.
If i navigate to the Action in my Web Application then i get the following response output to screen:
StatusCode: 200,
ReasonPhrase: 'OK',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers: { Pragma: no-cache X-SourceFiles: = XXXXXXX
Content-Disposition: attachment;
filename=GlobalOverview.csv
Content-Type: application/octet-stream Expires: -1
I take it i need to return a FileResult, but i have no clue as to how to convert the HttpResponseMessage to a FileResult.
In your MVC controller you can try returning a FileResult, and use the File() method reading the response of your API as byte array.
[Authorize]
[HttpGet]
public FileResult GlobalOverview()
{
HttpResponseMessage file = new HttpResponseMessage();
using (HttpClient httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(this.UserName + ':' + this.Password)));
Task<HttpResponseMessage> response = httpClient.GetAsync("api.someDomain/Reporting/GlobalOverview");
file = response.Result;
}
return File(file.Content.ReadAsByteArrayAsync().Result, "application/octet-stream", "GlobalOverview.csv");
}

Categories

Resources