How to properly do this C# MultiPartFormDataContent API Call? - c#

I am currently trying to make an api call in c# using a MultiPartFormDataContent but I keep
getting the following error:
"Response: {"statusCode":400,"error":"Bad Request","message":""Image file" must be of type object","validation":{"source":"payload","keys":["images"]}}"
This is my Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Example
{
public class Test
{
public static void Main(string[] args)
{
Task<string> test = testFunction();
Console.WriteLine("Result: " + test.Result);
}
public static async Task<string> testFunction()
{
const string file = "C:\\ExamplePath\\image_1.jpeg";
const string URL = "https://example-api?api-key=example-key";
string boundary = "---8d0f01e6b3b5dafaaadaad";
MultipartFormDataContent multipartContent = new MultipartFormDataContent(boundary);
var streamContent = new StreamContent(File.Open(file, FileMode.Open));
var stringContent = new StringContent("flower");
multipartContent.Add(stringContent, "organs");
multipartContent.Add(streamContent, "images");
try
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(URL, multipartContent);
Console.WriteLine("Response: " + await response.Content.ReadAsStringAsync());
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine("IN METHIOD: " + content);
return content;
}
return null;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
}
}
It's obviously a problem with how I am trying to do the api call but I don't know how to do it with an object instead like mentioned the error message.

This link has some good examples and where I actually got my code snippet from below.
Here's a basic example of using MultiFormDataContent:
HttpClient httpClient = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(new StringContent(username), "username");
form.Add(new StringContent(useremail), "email");
form.Add(new StringContent(password), "password");
form.Add(new ByteArrayContent(file_bytes, 0, file_bytes.Length), "profile_pic", "hello1.jpg");
HttpResponseMessage response = await httpClient.PostAsync("PostUrl", form);
response.EnsureSuccessStatusCode();
httpClient.Dispose();
string sd = response.Content.ReadAsStringAsync().Result;
I hope this helps or points you in the right direction.

Related

How can I query Github repository and issues using GraphQL and C#?

I want to query GitHub repository and issues using GraphQL and C#. When I query GitHub repository and issues, VS Code keeps erroring out error CS1003: Syntax error, ',' expected. If I replace the repository query with a simple query(viewer{login}), it works fine.
using GraphQL;
using Newtonsoft.Json;
using System;
using System.Text;
using System.Net.Http.Headers;
class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.github.com/graphql")
};
httpClient.DefaultRequestHeaders.Add("User-Agent", "MyConsoleApp");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "MyPersonalToken");
var queryObject = new
{
query = #"
query {
repository(owner:"BT23", name:"demo-repo"){
issues(last:10){
edges{
node{
title
}
}
}
}
}",
variables = new { }
};
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
Content = new StringContent(JsonConvert.SerializeObject(queryObject), Encoding.UTF8, "application/json")
};
dynamic responseObj;
using (var response = await httpClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
responseObj = JsonConvert.DeserializeObject<dynamic>(responseString);
}
//Console.WriteLine(responseObj.data.viewer.login);
//Console.ReadLine();
}
}
Please advise.

How to handle multiple http requests using single instance of HttpClient?

after doing some research I got to know that it's best to instantiate HttpClient once and then reuse it's instance everwhere else.
My GetAsync requests are working absolutely fine. However, with PostAsync I'm getting error.
Please find my code below :
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Net.Mime;
namespace ConsoleApplication
{
public class Program
{
private static HttpClient client = new HttpClient();
public static async Task Main(string[] args)
{
string Event_MSG ="[{\"ID\":354,\"EventTimeStamp\":\"2022-05-
17T05:35:25\",\"Title\":\"Failedtoretrieveequipmentlogs\",\"ControllerId\":24,
\"Details\":\"UnabletoretrievelogsfromdeviceP25-SC-
023322.Cyclepoweratfieldunit.Iftheproblemreturns,contactyourHunterrepresentative.\",
\"ControllerName\":\"P25-SC-0233\"},{\"ID\":353,\"EventTimeStamp\":\"2022-05-
17T05:34:20\",\"Title\":\"Failedtoretrieveequipmentlogs\",\"ControllerId\":17,
\"Details\":\"UnabletoretrievelogsfromdeviceP25-SC-
022615.Cyclepoweratfieldunit.Iftheproblemreturns,contactyourHunterrepresentative.\",
\"ControllerName\":\"P25-SC-0226\"},{\"ID\":352,\"EventTimeStamp\":\"2022-05-
17T05:33:50\",\"Title\":\"Failedtoretrieveequipmentlogs\",\"ControllerId\":16,
\"Details\":\"UnabletoretrievelogsfromdeviceP25-SC-
022514.Cyclepoweratfieldunit.Iftheproblemreturns,contactyourHunterrepresentative.\",
\"ControllerName\":\"P25-SC-0225\"},{\"ID\":351,\"EventTimeStamp\":\"2022-05-
17T05:33:20\",\"Title\":\"Failedtoretrieveequipmentlogs\",\"ControllerId\":15,
\"Details\":\"UnabletoretrievelogsfromdeviceP25-SC-
022413.Cyclepoweratfieldunit.Iftheproblemreturns,contactyourHunterrepresentative.\",
\"ControllerName\":\"P25-SC-0224\"},{\"ID\":350,\"EventTimeStamp\":\"2022-05-
17T05:32:50\",\"Title\":\"Failedtoretrieveequipmentlogs\",\"ControllerId\":14,
\"Details\":\"UnabletoretrievelogsfromdeviceP25-SC-
022312.Cyclepoweratfieldunit.Iftheproblemreturns,contactyourHunterrepresentative.\",
\"ControllerName\":\"P25-SC-0223\"}]";
Console.WriteLine("Starting connections");
List<KeyValue> keyvalue = JsonConvert.DeserializeObject<List<KeyValue>>(Event_MSG);
JArray EventMSG = JArray.Parse(Event_MSG);
int i = 0;
foreach(var item in EventMSG)
{
var inc_val = i++;
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Basic","auth_val");
HttpResponseMessage get_response = await
client.GetAsync("https://myurl.com/"+keyvalue[inc_val]);
var get_responseString = await get_response.Content.ReadAsStringAsync();
JObject obj = JObject.Parse(get_responseString);
string DeviceID = (string) obj["managedObject"]["id"];
string isoTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string c8y_payloadString = $"{{\"time\": \"{isoTime}\",\"source\": {{\"id\": \"
{DeviceID}\" }},\"type\": \"c8y_Golf_Controller\",\"text\": \"PilotCC Event\"}}";
JObject c8y_payloadObject = JObject.Parse(c8y_payloadString);
var c8y_finalPayload = new JObject();
c8y_finalPayload.Merge(item);
c8y_finalPayload.Merge(c8y_payloadObject);
string c8y_finalPayloadString = JsonConvert.SerializeObject(c8y_finalPayload);
Console.WriteLine(get_responseString);
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.com.nsn.cumulocity.event+json");
var stringContent = new StringContent(c8y_finalPayloadString, Encoding.UTF8,
MediaTypeNames.Application.Json);
stringContent.Headers.ContentType.CharSet = "";
HttpResponseMessage response = await client.PostAsync("https://myurl.com",
stringContent);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Console.WriteLine("Connections done");
}
}
public class KeyValue
{
[JsonProperty("ControllerName")]
public string ControllerName { get; set; }
public override string ToString()
{
return String.Format(ControllerName);
}
}
}
Now If I remove the following lines from the code, it works completely fine and returns correct json response for all 5 get requests :
POST REQUEST CODE
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.com.nsn.cumulocity.event+json");
var stringContent = new StringContent(c8y_finalPayloadString, Encoding.UTF8,
MediaTypeNames.Application.Json);
stringContent.Headers.ContentType.CharSet = "";
HttpResponseMessage response = await client.PostAsync("https://myurl.com", stringContent);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
But when I'm using this it throws an error : -
Unhandled exception. Newtonsoft.Json.JsonReaderException: Error reading JObject from JsonReader. Path '', line 0, position 0.
at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)
at Newtonsoft.Json.Linq.JObject.Parse(String json, JsonLoadSettings settings)
at Newtonsoft.Json.Linq.JObject.Parse(String json)
at ConsoleApplication.Program.Main(String[] args) in Program.cs:line 31
at ConsoleApplication.Program.<Main>(String[] args)
Only the first iteration runs successfully making both GetAsync and PostAsync calls, after that I get the above error.
Line which is throwing error is : JObject obj = JObject.Parse(get_responseString); and it is because in second iteration the get request is not returning any response(happening only when the lines in POST REQUEST CODE are included)
Sample Get Request response : -
{"managedObject":{"self":"https://url.co/inventory/managedObjects/43432113",
"id":"43432113"},"externalId":"P25-SC-0233","self":"https://url.co/identity/externalIds/c8y_Serial/P25-SC-0233",
"type":"c8y_Serial"}
What am I missing here?

Cannot Upload File using C# HttpClient, Postman works OK

I am trying to post a file to an iManage server REST interface (Apache server, java backend?? not sure). Postman works fine, but when I try it from C# .NET CORE 3.1 I get a response like so:
{
"error": {
"code": "FileUploadFailure",
"message": "File upload failure"
}
}
Anyone have any ideas I can try? Thanks!
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.IO;
using System.Text;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Uri url = new Uri("https://iManageServer.net/");
string filename = #"C:\Temp\temp.txt";
string token = "E4vt1DzXcnkQTmOUspN6TG6KLR7TClCPPbjyvHsu9TRlKvND9gO4xTPYIEYy0+Lu";
const string folderId = "MyFolderId";
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var content = new MultipartFormDataContent($"{DateTime.Now.Ticks:x}"))
{
var jsonString = JsonConvert.SerializeObject(new { warnings_for_required_and_disabled_fields = true, doc_profile = new { name = Path.GetFileNameWithoutExtension(filename), extension = Path.GetExtension(filename).TrimStart('.'), size = fs.Length } });
HttpContent httpContent = new StringContent(jsonString, Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var c1 = httpContent;
content.Add(c1, "\"json\"");
var c2 = new StreamContent(fs);
c2.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
content.Add(c2, "\"file\"");
c2.Headers.ContentDisposition.FileName = $"\"{filename}\"";
c2.Headers.ContentDisposition.FileNameStar = null;
var hch = new HttpClientHandler();
hch.ServerCertificateCustomValidationCallback += (sender, cert, chain, error) => true;
using (var httpClient = new HttpClient(hch) { BaseAddress = url })
{
httpClient.DefaultRequestHeaders.Add("User-Agent", "PostmanRuntime/7.26.5");
httpClient.DefaultRequestHeaders.Add("Accept", "*/*");
httpClient.DefaultRequestHeaders.Add("Connection", "keep-alive");
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"folders/{folderId}/documents"))
{
requestMessage.Headers.Add("X-Auth-Token", token);
requestMessage.Content = content;
var response = await httpClient.SendAsync(requestMessage);
string jsonResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
//never hits
}
else
{
System.Diagnostics.Debug.WriteLine(jsonResponse);
//{
// "error": {
// "code": "FileUploadFailure",
// "message": "File upload failure"
// }
//}
}
}
}
}
}
}
}
}
Postman works fine. Here is what the Wireshark trace looks like for both:
Postman is First then the C# result:
The Boundary on the MultipartFormDataContent was quoted. The iManage API did not like that.
I had to add the following code right after the instantiation of the content:
var boundary = $"-------------------------{DateTime.Now.Ticks:x}";
content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", $"multipart/form-data; boundary={boundary}");
content.GetType().BaseType.GetField("_boundary", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(content, boundary);

c# - DownloadString with WebClient - 404 Error

I am trying to download string from discord webhook :("https://discordapp.com/api/webhooks/704770710509453432/GdR4absQHKDKjUiNMpw3aIpX2tx-9Z7nmPE2Sn3TUkQDM12zUczaV-m80orh7WGVzvGK")
When I normally open the site with browser string is : {"message": "Unknown Webhook", "code": 10015}
But when I do that with WebClient:
WebClient wc = new WebClient();
string site = "https://discordapp.com/api/webhooks/704770710509453432/GdR4absQHKDKjUiNMpw3aIpX2tx-9Z7nmPE2Sn3TUkQDM12zUczaV-m80orh7WGVzvGK";
string x = wc.DownloadString(site);
It gives an "404 Error". Is there a way to get that {"message": "Unknown Webhook", "code": 10015} string with c#?
A rough guess would be that it's to do with the accept header. Check the documentation for the api, but my browser sent an additional 17 headers along with the request.
The simple answer would be, use HttpClient. It's recommended for new development and also gives the correct response here:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace discordapi_headers
{
class Program
{
static async Task Main(string[] args)
{
var client = new HttpClient();
var response = await client.GetAsync("https://discordapp.com/api/webhooks/704770710509453432/GdR4absQHKDKjUiNMpw3aIpX2tx-9Z7nmPE2Sn3TUkQDM12zUczaV-m80orh7WGVzvGK");
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
}
However that doesn't help if you can't. Also I'm intrigued now...
Ha! I should have listened to my own advice. For web service related stuff, you can't beat fiddler and postman. It turns out that the api is returning a custom 404 to the browser that has json content.
Unfortunately DownloadString sees the 404 and throws a WebException.
Here's an updated version that works, using HttpClient and WebClient.
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace discordapi_headers
{
class Program
{
static readonly string url = "https://discordapp.com/api/webhooks/704770710509453432/GdR4absQHKDKjUiNMpw3aIpX2tx-9Z7nmPE2Sn3TUkQDM12zUczaV-m80orh7WGVzvGK";
static async Task Main(string[] args)
{
Console.WriteLine(await UseHttpClient(url));
Console.WriteLine(await UseWebClient(url));
}
private static async Task<string> UseHttpClient(string url)
{
var client = new HttpClient();
var response = await client.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
private static async Task<string> UseWebClient(string url)
{
var client = new WebClient();
try
{
var response = await client.DownloadStringTaskAsync(url);
return response;
}
catch (WebException wex)
{
using (var s = wex.Response.GetResponseStream())
{
var buffer = new byte[wex.Response.ContentLength];
var contentBytes = await s.ReadAsync(buffer, 0, buffer.Length);
var content = Encoding.UTF8.GetString(buffer);
return content;
}
}
}
}
}

Microsoft AnalysisServices - start and stop server using Azure Functions

I wanted to write an Azure Function to stop and resume MS Analysis Sevice.
I have been tasked to do this using API Resume and API Suspend.
It's my first time with C# (I have Python/Javascript background).
What am I doing wrong? Can someone provide correct solution? Would be grateful from the bottom of my heart.
Here is my current code sample:
using System;
using System.Net;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
static HttpClient client = new HttpClient();
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req)
{
var subscriptionId = "mySId";
var resourceGroupName = "myRGName";
var serverName = "mySName";
var APIurl = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.AnalysisServices/servers/{serverName}/resume?api-version=2017-08-01";
var response = await client.PostAsync(APIurl, null);
string result = await response.Content.ReadAsStringAsync();
return req.CreateResponse(HttpStatusCode.OK);
}
This is what I get in console. Server is not stopping though.
As the api "Resume" you mentioned in your question requires Azure Active Directory OAuth2 Flow, so you need to get the access token first by referring to this tutorial and then use the access token to request this api in your function code.
In the code of your function app, you need to set the access token which we get above as the value of Authorization and then do the post reuqest.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
For those in times of need - this is my complete working solution.
using System;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
using System.Net;
using System.Net.Http;
using System.Collections.Generic;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ManageAS
{
public class ManageAS
{
string accessToken = null;
string responseString = null;
string serverStatus = null;
string serverFullURI = Environment.GetEnvironmentVariable("FULL_SERVER_URI");
static string shortServerName = Environment.GetEnvironmentVariable("SHORT_SERVER_NAME");
string resourceURI = "https://management.core.windows.net/";
string clientID = Environment.GetEnvironmentVariable("CLIENT_ID");
string clientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
string tenantID = Environment.GetEnvironmentVariable("TENANT_ID");
static string resourceGroupName = Environment.GetEnvironmentVariable("RESOURCE_GROUP_NAME");
static string subscriptionId = Environment.GetEnvironmentVariable("SUBSCRIPTION_ID");
static Uri apiURI = null;
[FunctionName("PerformAction")]
public async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req,
ILogger log)
{
await GetAccessToken();
await GetServerStatus(accessToken);
if (serverStatus == "Paused")
{
apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/resume?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
this.responseString = await ServerManagement(apiURI, accessToken);
}
if (serverStatus == "Succeeded")
{
apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/suspend?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
this.responseString = await ServerManagement(apiURI, accessToken);
}
return req.CreateResponse(HttpStatusCode.OK, responseString);
}
protected async Task<string> GetAccessToken()
{
string authority = "https://login.microsoftonline.com/" + tenantID;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
var token = await authenticationContext.AcquireTokenAsync(resourceURI, new ClientCredential(clientID, clientSecret));
accessToken = token.AccessToken;
return accessToken;
}
protected async Task<string> ServerManagement(Uri url, string token)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.PostAsync(url.ToString(), null);
response.EnsureSuccessStatusCode();
HttpContent content = response.Content;
string json;
json = await content.ReadAsStringAsync();
return json;
}
protected async Task<string> GetServerStatus(string token)
{
var url = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}?api-version=2017-08-01", subscriptionId, resourceGroupName, shortServerName));
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.GetAsync(url.ToString());
response.EnsureSuccessStatusCode();
string sJson = await response.Content.ReadAsStringAsync();
var dictResult = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(sJson);
if (!dictResult.ContainsKey("properties")) return null;
var dictProperties = dictResult["properties"] as Newtonsoft.Json.Linq.JObject;
if (dictProperties == null || !dictProperties.ContainsKey("state")) return null;
serverStatus = Convert.ToString(dictProperties["state"]);
return serverStatus;
}
}
}

Categories