C# - httpclient - json body - Not getting applied appropriately using httpcontent - c#

The following is the code from where I would return a tuple of response status code and response output.
private Tuple<int, string> API_Check(string URL, string reqtype, string reqbody, string split_username, string split_pwd)
{
string responsetxt="";
HttpResponseMessage httpresult = new HttpResponseMessage();
int statuscode = 0;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
HttpClient _httpClient = new HttpClient();
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username+":" + split_pwd));
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
try
{
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))
{
if (reqtype == "GET")
{
httpresult = _httpClient.GetAsync(URL).Result;
}
if (reqtype == "PUT")
{
httpresult = _httpClient.PutAsync(URL, content).Result;
//httpresult = _httpClient.PutAsync()
}
if (reqtype == "POST")
{
httpresult = _httpClient.PostAsync(URL, content).Result;
}
statuscode = (int)httpresult.StatusCode;
responsetxt = httpresult.Content.ReadAsStringAsync().Result;
return Tuple.Create(statuscode, responsetxt);
}
}
catch (System.Net.WebException Excptn)
{
statuscode = 401;
responsetxt = Excptn.Status.ToString();
using (var stream = Excptn.Response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
MessageBox.Show(reader.ReadToEnd());
}
}
return Tuple.Create(statuscode, responsetxt);
}
For some reason, the request body is not getting filled correctly during the call. I'm getting 401 Unauthorized as for this Post call, which is definitely not a authorization error as the response message that I receive is equivalent to empty body or invalid input json format.
When I tried to hit the same reqbody for the endpoint with Postman, I'm getting 200 with valid response. Also, the GetAsync works for a similar API which doesn't require a body.
I verified there is no issues with the username, password or the Endpoint URL.
Is there a way, I could avoid using httpcontent and use the string as it is for hitting the API through C#?
Now, I could not use HttpWebRequest due to my current .Net framework limitations.

There are many issues with your code:
Primarily, you are serializing reqbody which is already a string. It sounds like you have a JSON string already, in which case you don't need to serialize it.
Don't use .Result, it can cause a deadlock. use await instead.
Use Valuetuples instead of Tuple, which can be inefficient.
Do not set ServicePointManager.SecurityProtocol = ..., instead let the operating system choose the best security protocol.
Do not use ServicePointManager in general, as it affects all HTTP request from your app. Instead set the relevant HtppClient property, or better: use HttpRequestMessage and set it directly on the message.
You can simplify the code a bit if you use HttpRequestMessage, giving it the type of HTTP method
You are catching the wrong exception type. You should be catching HttpRequestException, from which you can get the actual StatusCode.
HttpClient by default does not throw on non-success codes. You need to handle them explicitly.
Cache the HttpClient, or you could get socket exhaustion.
Creating a new HttpResponseMessage doesn't make a huge amount of sense.
HttpClient _httpClient = new HttpClient {
DefaultRequestHeaders = {
ExpectContinue = false,
},
};
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, string reqbody, string split_username, string split_pwd)
{
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username + ":" + split_pwd));
try
{
using (var content = new StringContent(reqbody))
using (var request = new HttpRequestMessage(URL, reqtype))
{
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
if (reqtype != "GET")
message.Content = content;
using var httpresult = await _httpClient.SendAsync(URL, content);
var statuscode = (int)httpresult.StatusCode;
var responsetxt = await httpresult.Content.ReadAsStringAsync();
if (!httpresult.IsSuccessStatusCode)
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
catch (HttpRequestException ex)
{
var statuscode = ex.StatusCode ?? 0;
var responsetxt = ex.Message;
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
If you actually have an object to serialize then change the method to
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, object reqbody, string split_username, string split_pwd)
{
....
....
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))

Related

C# HttpClient.SendAsync causes error, cannot send a content-body with this verb-type

I am getting error cannot send a content-body with this verb-type. I am calling a GET Endpoint from a C# VSTO desktop application. What am I doing wrong.
public static string GetCentralPath(LicenseMachineValidateRequestDTO licenseMachine)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Properties.Settings.Default.Properties["JWT"].DefaultValue.ToString());
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri($"{Constants.URL.APIBase}licensemachine/GetCentralPath"),
Content = new StringContent(JsonConvert.SerializeObject(licenseMachine), Encoding.UTF8, "application/json"),
};
using (HttpResponseMessage response = client.SendAsync(request).GetAwaiter().GetResult()) // Causing ERROR
{
var result = GetStringResultFromHttpResponseMessage(response, true);
if (string.IsNullOrEmpty(result))
return null;
return JsonConvert.DeserializeObject<string>(result);
}
}
}
The end point looks like the following:
[HttpGet("GetCentralPath")]
public async Task<IActionResult> GetCentralPath(LicenseMachineValidateRequestDTO dto)
{
// Some code
}
fix the action, you cannot send body data with get, see this post
HTTP GET with request body
[HttpPost("GetCentralPath")]
public async Task<IActionResult> GetCentralPath(LicenseMachineValidateRequestDTO dto)
and fix request , replace Method = HttpMethod.Get with Post, this is what generates an error
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{Constants.URL.APIBase}licensemachine/GetCentralPath"),
Content = new StringContent(JsonConvert.SerializeObject(licenseMachine), Encoding.UTF8, "application/json"),
};

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);
}

.NET How to Send HttpClient Get Request on Specified IP address in same domain

I have a problem. I migrate from WebRequest to HttpClient.
In WebRequest i simple implement:
var webRequest = (HttpWebRequest)WebRequest.Create("http://111.111.111.111:8080/method");
webRequest.Host = "mysite:8080.com";
And http requests works fine.
Then i replace WebRequest on HttpClient, and faced with the problem.
var client = new HttpClient();
client.DefaultRequestHeaders.Host = "mysite:8080.com";
var result = await client.GetAsync("http://111.111.111.111:8080/method", cancellationToken).ConfigureAwait(false); // rise 404 error
GetAsync rise 404 in this case. HttpClient can't send requests on specified ip's?
This might be old, but to those who can bump in this error, here you can use this method :
static async void getHttpClient()
{
string mUrl = "http://192.168.1.111/method";
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(mURL))
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
if (result != null)
{
Console.WriteLine(result);
} else
{
//
// ERROR
//
}
}
}
happy codings

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