C# HttpClient request API suite - c#

I'm trying to develop an API suite so I can call it by passing some parameters. I successfully made the part about GET method.
POST seems a bit more complicated and I didn't find any source so I can understand how to make it. My plan is to call in POST with a body as parameter
I didn't develop the API I'm going to POST but it's not even a public one but inside the project development. Using .NET core 5.0. The scope is to call some APIs as regression tests before any deploy from the dev team and some of them might return a data that will be used as body param in the next API so I'm developing a console app that check if everything is OK.
This is the GET request I developed, could you help me understand how to make it using POST method?:
static void Main(string[] args)
{
string url = ConfigurationManager.AppSettings["url"];
string jsonBody = "";
string method = "GET";
string result = CallApi(url, method, jsonBody);
}
public static string CallApi(string url, string method, string bodyInput)
{
string bodyOutput = null;
HttpClient client = new();
Uri uri = new(url);
switch (method)
{
case "GET":
Task<HttpResponseMessage> response = client.GetAsync(uri);
HttpResponseMessage result = response.Result;
HttpContent content = result.Content;
bodyOutput = content.ReadAsStringAsync().Result;
break;
case "POST":
//bodyInput
break;
case "PUT":
break;
case "DELETE":
break;
default:
break;
}
return bodyOutput;
}
}
Thanks

Hi your code should look like this:
static async Task Main(string[] args)
{
string baseAddress = ConfigurationManager.AppSettings["baseAddress"];
string path = ConfigurationManager.AppSettings["path"];
string jsonBody = "";
HttpMethod method = HttpMethod.Get;
string result = await CallApi(baseAddress, path, method, jsonBody);
}
public static async Task<string> CallApi(string baseAddress, string path, HttpMethod method, string body)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = null;
switch (method)
{
case HttpMethod.Get:
response = await client.GetAsync(path);
break;
case HttpMethod.Post:
response = await client.PostAsJsonAsync(path, body);
break;
case HttpMethod.Put:
response = await client.PutAsJsonAsync(path, body);
break;
case HttpMethod.Delete:
response = await client.DeleteAsync(path);
break;
default:
throw new NotImplementedException();
}
// Throw an exception if the response code is not successful
response.EnsureSuccessStatusCode();
// Read the response
string bodyOutput = await response.Content.ReadAsStringAsync();
return bodyOutput;
}
}
You can see a more detailed example here:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client

call this function using await , unlike what your doing in your function.
Post :
static async Task<string> Postfunc()
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new object obj());
var data = new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json");
var client = new HttpClient();
var response = await client.PostAsync(url, data);
string result = await response.Content.ReadAsStringAsync();
return result;
}

Related

PostAsync API giving Bad Gateway

I am invoking a third party POST API from my own API (again POST METHOD). The third party API is having a security key, and it is working fine on the POSTMAN tool. However, when I tries to invoke through code, I am getting error, 'Bad Gateway'. Following is the code which I tried.
public static async Task<string> GetDetailsfromThirdParty(string kszstrng)
{
string contentstring = string.Empty;
using (var client = new HttpClient())
{
string baseURL = "https://abcde.kz.in/b2/vhsearch-all";
string prms = kszstrng;// input parameters to API, in JSON Format- this is JSON String.
try
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("key", "value");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
byte[] messageBytes = System.Text.Encoding.UTF8.GetBytes(prms);
var content = new ByteArrayContent(messageBytes);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(baseURL, content).ConfigureAwait(false);
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
contentstring = result;
}
}
catch (Exception ex)
{
string msg = ex.Message.ToString();
}
return contentstring;
}
}
I am getting error on this line:
var response = await httpClient.PostAsync(baseURL, content).ConfigureAwait(false);
While trying to execute I am getting the below error:
Not able to find out what's the issue? There is no network / Fireawall blockage. I have cross-verified with Systems Team as well.
Please suggest any issue with the code.
First of all, i recommend you to not declare the HttpClient in a using statement since this can cause a socket exhaustion (because the connections will stay open).
(see the docs for details)
Go for a static HttpClient (or use the IHttpClientFactory if you're project is .net Core).
I can't test your code since I'm not able to access this api.
But give it a try using a cleaner approach:
// static HttpClient
private static readonly HttpClient _HttpClient = new HttpClient();
// Can be used to set the baseUrl of the HttpClient from outside
public static void SetBaseUrl(Uri baseUrl)
{
_HttpClient.BaseAddress = baseUrl;
}
public static async Task<string> GetDetailsfromThirdParty(string kszstrng)
{
string contentstring = string.Empty;
string baseURL = "https://abcde.kz.in/b2/vhsearch-all";
string prms = kszstrng; // input parameters to API, in JSON Format- this is JSON String.
try
{
// Be aware of which headers you wanna clean if using the static HttpClient
_HttpClient.DefaultRequestHeaders.Accept.Clear();
_HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_HttpClient.DefaultRequestHeaders.Add("key", "value");
_HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
byte[] messageBytes = Encoding.UTF8.GetBytes(prms);
var content = new ByteArrayContent(messageBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _HttpClient.PostAsync(baseURL, content).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
contentstring = result;
}
}
catch (Exception ex)
{
// your exception handling
}
return contentstring;
}
Issue resolved. While forming the object to JSON String, there was an opening and closing angle brackets ([,]). Even though this is coming automatically while converting to JSON string, this was not accepted string at the vendor end. So I removed it and works perfectly. Thanks every one for the support.

HttpClient post returns bad request in c#, works in postman

I'm trying to access a rest endpoint, https://api.planet.com/auth/v1/experimental/public/users/authenticate. It is expecting json in the request body.
I can get the request to work in Postman but not using c#. Using postman I get the expected invalid email or password message but with my code I get "Bad Request" no matter I try.
Here is the code that makes the request
private void Login()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://api.planet.com/");
client.DefaultRequestHeaders.Accept.Clear();
//ClientDefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
Data.User user = new Data.User
{
email = "myemail#company.com",
password = "sdosadf"
};
var requestMessage = JsonConvert.SerializeObject(user);
var content = new StringContent(requestMessage, Encoding.UTF8, "application/json");
var response = client.PostAsync("auth/v1/experimental/public/users/authenticate", content).Result;
Console.WriteLine(response.ToString());
}
catch (WebException wex )
{
MessageBox.Show(wex.Message) ;
}
}
class User
{
public string email;
public string password;
}
Here are screen grabs form Postman that are working
The way to get this to work was to alter the content header "content-type". By default HTTPClient was creating content-type: application/json;characterset= UTF8. I dropped and recreated the content header without the characterset section and it worked.
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/json");
The issue is you are trying to call an async method without waiting for the response using await method or var task = method; task.Wait() Therefore, when you end up doing response.ToString() it returns the text you are seeing.
One way to handle this within a non-async method would be to do the following:
var task = client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
task.Wait();
var responseTask = task.Content.ReadAsStringAsync();
responseTask.Wait();
Console.WriteLine(responseTask.Result);
Another way is to make the current method async by doing private async void Login() and then do:
var postResp = await client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
var response = await postResp.Content.ReadAsStringAsync();
Console.WriteLine(response);
Create a Method Like this...
static async Task<string> PostURI(Uri u, HttpContent c)
{
var response = string.Empty;
var msg = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.PostAsync(u, c);
msg = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
call In your Method
public void Login()
{
string postData ="{\"email\":\"your_email\",\"password\":\"your_password\"}";
Uri u = new Uri("yoururl");
var payload = postData;
HttpContent c = new StringContent(payload, Encoding.UTF8,"application/json");
var t = Task.Run(() => PostURI(u, c));
t.Wait();
Response.Write(t.Result);
}

how to make an OPTIONS request with HttpClient

How do I send an OPTIONS request with System.Net.Http.HttpClient
exposed methods for HttpClient
DeleteAsync
GetAsync
PostAsync
PutAsync
few others as well.....
I was expecting a OptionsAsync
switch (httpMethod) {
case HTTP_METHODS.DELETE:
{
httpResponseMessage = httpClient.DeleteAsync(uri).Result;
break;
}
case HTTP_METHODS.GET:
{
httpResponseMessage = httpClient.GetAsync(uri).Result;
break;
}
case HTTP_METHODS.POST:
{
httpResponseMessage = httpClient.PostAsync(uri, httpContent).Result;
break;
}
case HTTP_METHODS.PUT:
{
httpResponseMessage = httpClient.PutAsync(uri, httpContent).Result;
break;
}
case HTTP_METHODS.OPTION:
{
//not sure what method to call on httpclient here to make Options request
httpResponseMessage = httpClient.PutAsync(uri, httpContent).Result;
if (httpResponseMessage.Headers.Contains("X-CSRF-TOKEN")) {
IEnumerable < string > headerValues = httpResponseMessage.Headers.GetValues("X-CSRF-TOKEN");
csrfToken = headerValues.FirstOrDefault();
}
break;
}
}
There are no wrappers for that kind of methods (e.g. OPTIONS and HEAD), but you could use SendAsync just like these wrappers do:
var request = new HttpRequestMessage(HttpMethod.Options, "url");
var result = await _httpClient.SendAsync(request);
In order to get the expected response back, you have to make sure to set the "Origin" header in the request, like so:
using var client = new HttpClient();
var message = new HttpRequestMessage(HttpMethod.Options, "url");
message.Headers.Add("Origin", "http://example.com");
var response = await client.SendAsync(message);

Application works in Debug - Fails when compiled

I have a situation I just don't understand.
I have a c# class library application that works perfectly when I step through it in debug mode within VS2017.
When I compile it in "Debug" mode and call it from a console app it fails when calling a https API endpoint and does not report any error.
This is how I am calling the "CreateOrder":
// send the order to DNAGenotek
DNAGenotek.Model.Order.CreateResponse.CreateOrderResponse orderResponse = await DNAGenotek.Orders.CreateOrder(DNAGenotekAPIKey, DNAGenotekOrder);
This is how I am calling the "CallAPIString" function:
public static async Task<Model.Order.CreateResponse.CreateOrderResponse> CreateOrder(string APIKey, Model.Order.Create.Order order)
{
// convert the object to a string
string orderAsString = JsonConvert.SerializeObject(order);
// create the order
string result = await Helpers.CallAPIString(APIKey, "https://sofia-stage.dnagenotek.com/api/v2/orders", "POST", orderAsString);
DNAGenotek.Model.Order.CreateResponse.CreateOrderResponse orderResponse = JsonConvert.DeserializeObject<Model.Order.CreateResponse.CreateOrderResponse>(result);
return orderResponse;
}
I have attached to the running console .exe and stepped through and it fails on this line when doing a POST
WebResponse httpResponseGETDELETE = await httpWebRequest.GetResponseAsync();
In the following code:
public static async Task<string> CallAPIString(string APIKey, string url, string method, string bodyContent = null)
{
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.Method = method;
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization", "Basic " + APIKey);
switch (method)
{
case "GET":
case "DELETE":
WebResponse httpResponseGETDELETE = await httpWebRequest.GetResponseAsync();
using (StreamReader streamReader = new StreamReader(httpResponseGETDELETE.GetResponseStream()))
{
string result = await streamReader.ReadToEndAsync();
return result;
}
case "POST":
case "PUT":
case "PATCH":
using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
await streamWriter.WriteLineAsync(bodyContent);
await streamWriter.FlushAsync();
streamWriter.Close();
WebResponse httpResponsePOSTPUT = await httpWebRequest.GetResponseAsync();
using (StreamReader streamReader = new StreamReader(httpResponsePOSTPUT.GetResponseStream()))
{
string result = await streamReader.ReadToEndAsync();
return result;
}
}
default:
return "Method Not Found!";
}
}
catch (Exception e)
{
return e.Message;
}
}
It doesn't even bubble up to the catch line. It just closes the app.
When I watch the network traffic in Fiddler I can see it attempting to open the https connection but it then stalls.
This is what I see in Fiddler:
I am complete stumped how to get to the root cause without an error message?
UPDATE
I have now rewritten my code using the HttpClient and it now fails at the line:
HttpResponseMessage response = await client.PostAsync(url, content);
in this code:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Basic " + APIKey);
StringContent content = new StringContent(bodyContent, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(url, content);
return await response.Content.ReadAsStringAsync();
}
OK - this was my fault.
In the console application I was using :
SyncEngine.Functions.Sync("APIKey1", "APIKey2");
When I should have been using:
SyncEngine.Functions.Sync("APIKey1", "APIKey2").GetAwaiter().GetResult();
Sorry guys

Receiving JSON data back from HTTP request

I have a web request that is working properly, but it is just returning the status OK, but I need the object I am asking for it to return. I am not sure how to get the json value I am requesting. I am new to using the object HttpClient, is there a property I am missing out on? I really need the returning object. Thanks for any help
Making the call - runs fine returns the status OK.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMsg = client.GetAsync(string.Format("http://localhost:5057/api/Photo")).Result;
The api get method
//Cut out alot of code but you get the idea
public string Get()
{
return JsonConvert.SerializeObject(returnedPhoto);
}
If you are referring to the System.Net.HttpClient in .NET 4.5, you can get the content returned by GetAsync using the HttpResponseMessage.Content property as an HttpContent-derived object. You can then read the contents to a string using the HttpContent.ReadAsStringAsync method or as a stream using the ReadAsStreamAsync method.
The HttpClient class documentation includes this example:
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Building on #Panagiotis Kanavos' answer, here's a working method as example which will also return the response as an object instead of a string:
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json; // Nuget Package
public static async Task<object> PostCallAPI(string url, object jsonObject)
{
try
{
using (HttpClient client = new HttpClient())
{
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
if (response != null)
{
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<object>(jsonString);
}
}
}
catch (Exception ex)
{
myCustomLogger.LogException(ex);
}
return null;
}
Keep in mind that this is only an example and that you'd probably would like to use HttpClient as a shared instance instead of using it in a using-clause.
Install this nuget package from Microsoft System.Net.Http.Json. It contains extension methods.
Then add using System.Net.Http.Json
Now, you'll be able to see these methods:
So you can now do this:
await httpClient.GetFromJsonAsync<IList<WeatherForecast>>("weatherforecast");
Source: https://www.stevejgordon.co.uk/sending-and-receiving-json-using-httpclient-with-system-net-http-json
I think the shortest way is:
var client = new HttpClient();
string reqUrl = $"http://myhost.mydomain.com/api/products/{ProdId}";
var prodResp = await client.GetAsync(reqUrl);
if (!prodResp.IsSuccessStatusCode){
FailRequirement();
}
var prods = await prodResp.Content.ReadAsAsync<Products>();
What I normally do, similar to answer one:
var response = await httpClient.GetAsync(completeURL); // http://192.168.0.1:915/api/Controller/Object
if (response.IsSuccessStatusCode == true)
{
string res = await response.Content.ReadAsStringAsync();
var content = Json.Deserialize<Model>(res);
// do whatever you need with the JSON which is in 'content'
// ex: int id = content.Id;
Navigate();
return true;
}
else
{
await JSRuntime.Current.InvokeAsync<string>("alert", "Warning, the credentials you have entered are incorrect.");
return false;
}
Where 'model' is your C# model class.
It's working fine for me by the following way -
public async Task<object> TestMethod(TestModel model)
{
try
{
var apicallObject = new
{
Id= model.Id,
name= model.Name
};
if (apicallObject != null)
{
var bodyContent = JsonConvert.SerializeObject(apicallObject);
using (HttpClient client = new HttpClient())
{
var content = new StringContent(bodyContent.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Add("access-token", _token); // _token = access token
var response = await client.PostAsync(_url, content); // _url =api endpoint url
if (response != null)
{
var jsonString = await response.Content.ReadAsStringAsync();
try
{
var result = JsonConvert.DeserializeObject<TestModel2>(jsonString); // TestModel2 = deserialize object
}
catch (Exception e){
//msg
throw e;
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
return null;
}
The code below is to access your HttpResponseMessage and extract your response from HttpContent.
string result = ret.Result.Content.ReadAsStringAsync().Result;
Convert your json in a structure according with your business
In my case BatchPDF is a complex object that it is being populated by result variable.
BatchPDF batchJson = JsonConvert.DeserializeObject<BatchPDF>(result);
return batchJson;

Categories