How to reuse HttpClient reference for multiple http requests - c#

I have a code where I am creating an instance of HttpClient, which is inside of a foreach loop. Which means it is creating a new instance for each time the iteration takes place. Here is my code :
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Net.Mime;
using System.Net.Http.Json;
public class Program
{
static async Task Main(string[] args)
{
//JSON String
string json = #"{
'Values': [
{
'MsgSource': null,
'TagName': 'Data.New_MSG',
'RawValue': '[\r\n {\r\n \'ID\': 145,\r\n \'StationNo\': 6,\r\n
\'RunTime\': 1800,\r\n \'ControllerID\': 4,\r\n
\'ControllerAddress\': 2,\r\n \'ProgramNo\': 2,\r\n
\'ModeID\': \'AutoProgram\',\r\n \'EventDate\': \'2022-04-
27T23:30:02\',\r\n \'Description\': \'Irrigation
Completed\',\r\n \'MessageCode\': 5\r\n,\r\n
\'ControllerName\': \'P25-SC-0233\' },\r\n {\r\n \'ID\':
144,\r\n \'StationNo\': 18,\r\n \'RunTime\': 1800,\r\n
\'ControllerID\': 4,\r\n \'ControllerAddress\': 2,\r\n
\'ProgramNo\': 5,\r\n \'ModeID\': \'AutoProgram\',\r\n
\'EventDate\': \'2022-04-27T22:00:00\',\r\n \'Description\':
\'Irrigation Completed\',\r\n \'MessageCode\': 5\r\n,\r\n
\'ControllerName\': \'P25-SC-0226\' }\r\n]',
'Status': 'Normal',
'ComStatus': null,
'TimeStamp': '2022-04-28 13:17:39.851'
}
]
}";
//Deserializing JSON String
Root root = JsonConvert.DeserializeObject<Root>(json);
//Extracting the value of only RawValue key from the String
string rawValue = root.Values[0].RawValue;
//Creating List of ControllerName key
List<Station> stations = JsonConvert.DeserializeObject<List<Station>>(rawValue);
JArray array = JArray.Parse(rawValue);
int i = 0; //Initializing Index to add ControllerName to the URL in Get http
//request below
foreach(var item in array) //Iterating through array
{
var inc_val = i++;
using (var client = new HttpClient()) //Here I've Instantiated HttpClient
{
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Basic","auth_value"); //Basic Auth
//Mandatory key for Message body in Post request
string isoTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
//Concatenating ControllerName in URL and making get request
HttpResponseMessage get_response = await
client.GetAsync("https://myurl.com/"+stations[inc_val]);
var get_responseString = await get_response.Content.ReadAsStringAsync();
JObject obj = JObject.Parse(get_responseString);
//Extracting id from the response of Get request which has to be used in
//Post Data when making Post request
string name = (string) obj["managedObject"]["id"];
//Required JSON Body structure which needs to be merged with Post Data
string json2 = $"{{\"time\": \"{isoTime}\",\"source\": {{\"id\": \"{name}\"
}},\"type\": \"c8y_Golf_Controller\",\"text\": \"PilotCC Data New Msg\"}}";
JObject json3 = JObject.Parse(json2);
var result = new JObject();
result.Merge(item);
result.Merge(json3);
string json4 = JsonConvert.SerializeObject(result);
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.com.nsn.cumulocity.event+json"); //More Headers
var stringContent = new StringContent(json4, Encoding.UTF8,
"application/json");
stringContent.Headers.ContentType.CharSet = "";
//Making Post request
HttpResponseMessage response = await client.PostAsync("https://myurl.com",
stringContent);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
}
}
public class Root
{
public List<Value> Values {get; set;}
}
public class Value
{
public string RawValue { get; set; }
}
public class Station
{
[JsonProperty("ControllerName")]
public string ControllerName { get; set; }
public override string ToString()
{
return String.Format(ControllerName);
}
}
}
How do I make it better by not creating unnecessary instances of HttpClient each time foreach loop iteration in triggered and instead reuse the single instance of HttpClient for making all the http requests??

In your case, it would be enough to ...
public class Program
{
static async Task Main()
{
// Create one single instance outside the loop ...
var client = new HttpClient();
// more code here
foreach( var item in array )
{
// ... then use it.
var response = await client.GetAsync(/* yadda yadda */);
}
}
}

Related

GraphQL request in C#

I'm a Unity developer tool and i want to post a GraphQL request with using System.Net.Http; (i don't want to use the GraphQL dll because there are compatibility problems).
But i have this error (got on the Debug.Log) :
POST body missing, invalid Content-Type, or JSON object has no keys.
My code :
static async Task<string> getEntityID(string path)
{
var values = new Dictionary<string, string>
{
{ "query", "query {topLevelEntityTypes {id}}" },
{ "variables", "{}" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://localhost:4000/graphql", content);
var responseString = await response.Content.ReadAsStringAsync();
Debug.Log(responseString);
return responseString;
}
Thank you !
Solution by ProgrammingLlama :
static async Task<string> getEntityID(string path)
{
var myObject = new QueryJson();
myObject.query = "query {topLevelEntityTypes {id}}";
myObject.variables = "{}";
var response = await client.PostAsync("http://localhost:4000/graphql", new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(myObject), Encoding.UTF8, "application/json"));
var responseString = await response.Content.ReadAsStringAsync();
Debug.Log(responseString);
return responseString;
}
Class json
internal class QueryJson
{
public string query { get; set; }
public string variables { get; set; }
}

C#- http client response help request

I have the following code:
static async Task checkIMEI( double IMEI)
{
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://kelpom-imei-checker1.p.rapidapi.com/api?service=model&imei=" + IMEI.ToString() ),
Headers =
{
{ "X-RapidAPI-Host", "kelpom-imei-checker1.p.rapidapi.com" },
{ "X-RapidAPI-Key", "key" },
}
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
object result = await response.Content.ReadAsStringAsync();
MessageBox.Show("\n" + result);
}
}
Running this code I get the following
response
I would like to further break up this response and the individual data and assign it to a variable such as
string ModelNum= model_nb >> should show "SM-G891A"
String Brand = brand >> should show "Samsung Korea"
Your help would be appriciated.
first your Client is bad practice use this link HttpClientFactory Microsoft docs to refactor your client.
Then Create Class for your needed model for ex:
public class Mobile
{
public string ModelNum { get; set; }
public string Brand { get; set; }
}
then you should deserialize your result to your model:
var result = await response.Content.ReadAsStringAsync();
var model = JsonSerializer.Deserialize<Mobile>(result);

c # send parameters when sending json get request

I'm working on an application to make api get, post, delete, update requests on c # windowsforms.
My problem is: I want to send a parameter in "Body" when requesting a get. How can I do that ?
using System.Net.Http;
using System;
using System.Threading.Tasks;
using HastaTakip.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Text;
namespace HastaTakip.Api
{
public class CustomersRepository
{
public HttpClient _client;
public HttpResponseMessage _response;
public HttpRequestMessage _requestMessage;
public CustomersRepository()
{
_client = new HttpClient();
_client.BaseAddress = new Uri("http://localhost:3000/");
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZ0aG1seW16QGhvdG1haWwuY29tIiwidXNlcklkIjoxLCJpYXQiOjE2MTM5MDY5NDMsImV4cCI6MTYxNDA3OTc0M30.NER1RMTYx41OsF26pjiMXY-pLZTE-pIg4Q73ehwGIhA");
_client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<CustomersModel> GetList()
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("business_code", "dental")
});
_response = await _client.GetAsync(content);
var json = await _response.Content.ReadAsStringAsync();
var listCS = CustomersModel.FromJson(json);
return listCS;
}
}
}
to send a GET request with a JSON body:
HttpClient client = ...
...
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("some url"),
Content = new StringContent("some json", Encoding.UTF8, ContentType.Json),
};
var response = await client.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
But HTTP GET with a body is a somewhat unconventional construct that falls in a gray area of the HTTP specification!
You better create a class for your content data:
public class RequestData
{
pubic string BusinessCode {get; set;}
{
After this you can create your content object
public async Task<CustomersModel> GetList()
{
var data=new RequestData{BusinessCode="dental"}
var stringData = JsonConvert.SerializeObject(data);
contentData = new StringContent(stringData, Encoding.UTF8, "application/json");
var response = await _client.GetAsync(contentData);
// but I am not sure that Get will work correctly so I recommend to use
var response = await _client.PostAsync(contentData);
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<CustomersModel>(stringData);
}
else
{
....error code
}
}

Returning JSON via WebApi

I am using the HttpClient in C# to try and integrate SSO (Single Sign On) into some of our custom Applications.
I have done this successfully in our JavaScript Apps, but I'm having some difficulty integrating it into some of our Umbraco sites.
My code so far:
using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using Umbraco.Web;
using Umbraco.Web.WebApi;
namespace Umbraco.WebApi
{
public class TestController : UmbracoApiController
{
public HttpClient client = new HttpClient();
[HttpPost]
public async Task<Object> GetRefreshToken(Token t)
{
try {
string refToken = t.refresh_token;
var values = new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", "CLIENTID" },
{ "client_secret", "CLIENTSECRET" },
{ "refresh_token", refToken }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://URL.org/Token", content);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
} catch(HttpRequestException e) {
return e;
}
}
public class Token
{
public string refresh_token { get; set; }
}
public class AuthData
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_int { get; set; }
public string refresh_token { get; set; }
public string userName { get; set; }
public string client_id { get; set; }
public DateTime issued { get; set; }
public DateTime expires { get; set; }
}
}
}
Which does successfully return the data I'm after but there are issues with the returned data (Removed sensitive data):
<z:anyType xmlns:d1p1="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" i:type="d1p1:string">
{"access_token":"XXXXXXXX","token_type":"bearer","expires_in":1199,"refresh_token":"XXXXXX","userName":"XXXXXX","as:client_id":"XXXXX",".issued":"Fri, 20 Sep 2019 13:23:48 GMT",".expires":"Fri, 20 Sep 2019 13:43:48 GMT"}
</z:anyType>
It also seems to be getting returned as XML instead of JSON?
C# is not my strongest of languages, so I may be completely wrong.
Any help appreciated.
In your code, after you get the JSON string responseString, instead of returning it, try the following code.
...
string responseString = await response.Content.ReadAsStringAsync();
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(responseString, Encoding.UTF8, "application/json");
return response;
You need to change your method return value from Task<Object> to Task<HttpResponseMessage>
Edit:
To access the properties, install the Newtonsoft.Json package and try below code.
var jsonString = ...
JObject jo = JObject.Parse(jsonString);
Console.WriteLine(jo["access_token"]);
One of the things you can do to resolve this is to specifically request a json format on your response by adding the corrent request headers
Accept: application/json
try it like so
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Now if the server pays attentions to the request headers it will return a json back to you.
It might be defaulting to xml because there is no such header in your request OR the server only supports returning xml responses.
EDIT: If you can not get the server to return json, you can convert your xml string response to json string. Take a look at this example. After the convertsion you can return your json string normally from your controller.
Edit:
Ok, try out this sample below:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", ""),
new KeyValuePair<string, string>("scope", ""),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("redirect_uri", ""),
new KeyValuePair<string, string>("code", ""),
new KeyValuePair<string, string>("client_secret","")
});
AADTokenResponse TokenResponse = null;
string _baseAddress = string.Format("https://yourTargetDomain.com/");
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage = await client.PostAsync("targetApiSegment", content);
if (responseMessage.IsSuccessStatusCode)
{
var responseString = await responseMessage.Content.ReadAsStringAsync();
TokenResponse = JsonConvert.DeserializeObject<AADTokenResponse>(responseString);
}
}
I think in this case, FormUrlEncodedContent is the wrong choice, and you'd want to use StringContent instead, similar to this:
var content = new StringContent(HttpUtility.UrlEncode(values), Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://URL.com/Token", content);
The reason being, I dont think FormUrlEncodeContent has an overload to accept adding content type.
Another alternative would be to switch to using SendAsync rather than PostAsync, as SendAsync has some additional flexibility.

Why do I get "not_authed"-error from Slack when sending a post-request from c#?

I am trying to serialize an object into Json and then send it to Slack. I have done this successfully without serializing but instead using "Dictionary" and "FormUrlEncodedContent" and then send it.
But now, for the purpose of making things easier and more agile, I just wanted to create one JSon-class which I could serialize and then use for every request I want to send.
Here is my code:
My JsonObject:
public class JsonObject
{
private string _token = "xoxp-MyToken";
[JsonProperty("token")]
public string token { get { return _token; } }
[JsonProperty("channel")]
public string channel { get; set; }
[JsonProperty("as_user")]
public bool as_user = true;
[JsonProperty("username")]
public string username { get;set; }
[JsonProperty("text")]
public string text { get; set; }
}
My client:
public class BpsHttpClient
{
private readonly HttpClient _httpClient = new HttpClient { };
public Uri UriMethod { get; set; }
public BpsHttpClient(string webhookUrl)
{
UriMethod = new Uri(webhookUrl);
}
public async Task<HttpResponseMessage> UploadFileAsync(StringContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
}
Main:
class MainArea
{
public static void Main( string[] args)
{
try
{
Task.WaitAll(SendMessage());
}
catch(Exception ass)
{
Console.WriteLine(ass);
Console.ReadKey();
}
}
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/chat.postMessage");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
JO.text = "This is so much fun :D !";
var Json = JsonConvert.SerializeObject(JO, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var StringJson = new StringContent(Json, Encoding.UTF8, "application/json");
var DeSon = JsonConvert.DeserializeObject(Json);
Console.WriteLine(DeSon); //this is for me to see if my JsonObject looks correct - it does ;)
Console.ReadKey();
var Response = await client.UploadFileAsync(StringJson);
string AnswerContent = await Response.Content.ReadAsStringAsync();
Console.WriteLine(AnswerContent);
Console.ReadKey();
}
}
When I run the code I allways get the answer:
Output:
{"ok":false,"error":"not_authed"}
although I think my JsonObject looks right - it has the token in there...
Anybody have an idea why?
So, i figured it out - I SHALL NOT put my token in the JsonObject I want to send.
The solution in this case (using httpclient) is that one has to add a header for authorization to the client, like so:
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "lé token");
and then it works.

Categories

Resources