System.Net.Http.HttpRequestException (while working with REST API) - c#

ExceptionSource: mscorlib, ExceptionMesage: An error occurred while
sending the request, ExceptionTrace: at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
atSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at d__4.MoveNext().
For most of the times, I could get response from API. Otherwise, occasionally I get the above stated error.
I see some answers (authentication problem, wrong request URL and some others), but I have made sure all those are properly configured at my end.
Hope I can get some help here. Here is the section where the error appears:
HttpResponseMessage response = await client.PostAsync(path, new StringContent(contents, Encoding.UTF8, "application/json")).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
Task<string> resultdata = response.Content.ReadAsStringAsync();
if (resultdata != null)
{
var resultContent = resultdata.Result;
JsonSerializerSettings settings = new JsonSerializerSettings();
obj reference = JsonConvert.DeserializeObject<Class Name>(resultContent, settings);
}
}

Related

Why does HttpClient SendAsync have empty content when an endpoint returns an error?

Service:
I have an endpoint that returns a custom "Error Details" json object when there is an error.
So for my Exception Middleware class I do something like this:
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var errorDetail = new ErrorDetail()
{
Title = "Some Title",
Detail = exception.Message
// some other fields
};
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var json = JsonSerializer.Serialize(errorDetail);
return context.Response.WriteAsync(json);
}
Client:
I consume the endpoint from a HttpClient in another service:
protected async Task<string> PostAsync(string path, string json)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(path, UriKind.Relative));
var postContent = new StringContent(json, Encoding.UTF8, "application/json");
httpRequestMessage.Content = postContent;
var httpResponseMessage = await _httpClient.SendAsync(httpRequestMessage);
// if error response I expect to get error details json, but instead get empty string:
var stringContent = await httpResponseMessage.Content.ReadAsStringAsync();
return stringContent;
}
For some reason httpResponseMessage.Content.ReadAsStringAsync() returns an empty string when trying to get the ErrorDetail json.
If I call the endpoint in Swagger, the errorDetail json is shown.
Strangely a different sort of error like 503 service unavailable and nginx returns error html, the .ReadAsStringAsync() will return the error html.
Why is httpResponseMessage.Content.ReadAsStringAsync() empty when there is an errorDetails json returned?
I'm using dotnet core 6 and aspnet core.

C# System.Net.Http.StringContent() gives exception during the run time

We have the following piece of code where we use constructor of System.Net.Http.StringContent(). This following code is executed by multiple threads in my application.
public static HttpContent GetContent(this object model)
{
var body = JsonConvert.SerializeObject(model, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
var content = new StringContent(body, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return content;
}
We are getting Null reference exception in the execution of above code. "body"
param is just a string variable which we confirmed it to be not null. The following is the stack trace:
System.NullReferenceException : Object reference not set to an
instance of an object. at System.Text.Encoding.get_WebName() at
System.Net.Http.StringContent..ctor(String content, Encoding encoding,
String mediaType) at
AW.Api.Client.Extensions.HttpExtensions.GetContent(Object model,
String contentType)
StringContent() is trying to access the WebName property of Encoding.cs and on seeing the library code of Encoding.cs,
https://referencesource.microsoft.com/#mscorlib/system/text/encoding.cs,00987bab2ca262fa
We can notice that while accessing the property "WebName", a private field "dataItem" is being referenced after a null-check. Can anyone please help in understanding what might go wrong here? Is there a possibility of race condition where "dataItem" is being referenced by one thread while other thread setting it to null.
It seems like this code line not the true exception line.
Try to check the LINE REFERENCED THE VARIABLE "content"
Here is the code example ,I assume you use System.Net.Http.HttpClient
using (HttpClient client = new HttpClient())
{
var content = new StringContent(body, Encoding.UTF8, "application/json");
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Clear();
// below is the variable content
var response = await client.PostAsync(url, content).ConfigureAwait(true);
if (!response.IsSuccessStatusCode) throw new FileNotFoundException();
var responseContent = response.Content;
return await responseContent.ReadAsStringAsync().ConfigureAwait(true);
}

HttpClient timeout error calling PostAsJsonAsync

I am having a problem getting the response from the HttpClient PostAsJsonAsync method. I am passing a StringContent data to a POST request but it returns a Forbidden (403) status code.
When I tried adding a default request header (commented line of code), the error changed and it returned a timeout issue:
{System.Net.Http.WinHttpException (0x80072EE2): The operation timed out
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.Tasks.RendezvousAwaitable`1.GetResult()
at System.Net.Http.WinHttpHandler.d__105.MoveNext()}
var content = new StringContent(JsonConvert.SerializeObject(contentBody), Encoding.UTF8, "application/json");
var client = new HttpClient();
client.BaseAddress = new Uri("https://www.example.com");
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var httpResponse = await client.PostAsJsonAsync("api/details", content))
{
if (httpResponse.Content != null)
{
var responseContent = await httpResponse.Content.ReadAsStringAsync();
}
}
When I tried it in Postman, it returned the JSON data. Why doesn't it work in the code? Is there something that blocks my connection?
The BaseAddress property needs to be suffixed with a forward slash:
client.BaseAddress = new Uri("https://www.example.com/");

HttpResponseMessage.Content.ReasAsString returns an empty string

I have a WS that I call like this:
HttpResponseMessage response = await client.PostAsync(url, new StringContent(json));
the WS will throw an Exception (Forbidden) and in my Blazor application that made the PostAsync call, I will get an HttpResponseMessage with response code 405, not sure why its 405, it should be 403 (Postman returns 403).
I have enabled CORS (ServiceStack code):
Plugins.Add(new CorsFeature(allowedOrigins: "*",
allowedMethods: "GET, POST, PUT, DELETE, OPTIONS",
allowedHeaders: "*",
allowCredentials: true));
This is some Console.Writeline I did, just before the PostAsync:
Failed to load resource: the server responded with a status of 405 ()
** UPDATED **
This are the two methods:
public async Task<TResponse> PostAsync<TResponse, TRequest>(string requestUri, TRequest request)
{
string url = GetUrl(requestUri);
Console.WriteLine("URI: " + url);
string json = JsonConvert.SerializeObject(request);
Console.WriteLine($"{url} | {json}");
HttpResponseMessage response = await client.PostAsync(url, new StringContent(json));
return await GetResponseOrThrowHttpException<TResponse>(response);
}
private async Task<T> GetResponseOrThrowHttpException<T>(HttpResponseMessage response)
{
Console.WriteLine($"GetResponseOrThrowHttpException: {response.StatusCode}");
string responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine($"GetResponseOrThrowHttpException ContentStringResult: |{responseString}|");
if (!response.IsSuccessStatusCode)
{
Newtonsoft.Json.Linq.JObject jsonObject = Newtonsoft.Json.Linq.JObject.Parse(responseString);
string responseStatusString = jsonObject["ResponseStatus"].ToString();
Console.WriteLine($"GetResponseOrThrowHttpException 4: {responseStatusString}");
ResponseStatus responseStatus = JsonConvert.DeserializeObject<ResponseStatus>(responseStatusString);
Console.WriteLine($"Throwing HttpException: {response.StatusCode} {responseStatus.Message}");
throw new HttpException(response.StatusCode, responseStatus.Message);
}
return JsonConvert.DeserializeObject<T>(responseString);
}
When I try to get the string value of the response, it is empty:
string responseString = await response.Content.ReadAsStringAsync();
and the responseString is an empty (length 0) string.
If I run the exact same request in Postman, I get a valid response:
So, the response JSON, seen at the bottom in the image above, is what I want to work with in the Blazor app, and parse it as a JSON object and move on from there.
I also note that I get here a 403 error, which I expected, and in the Blazor app, I get a 405.
Is this a CORS issue even though I have enabled CORS on the WS side?
I guess your content should be posted like that:
new StringContent(json, Encoding.UTF8, "application/json")
Even if this is not the cause of your suffering, it's better to use this so...
Hope this helps...
Verify that you have setup proper CORS configuration for the domain.
Looks like you made call to another domain:port combination from your Blazor application. Even if this C#, all security rules inside browser still applies.

Making POST request fails due to invalid or wrong certificate

I am trying to make a POST request in an UWP C# app, based on this example - Method A.
The code for my example is:
string scriptname = "myscript.php";
var content = new FormUrlEncodedContent(values);
//Exception Line (103):
var response = await client.PostAsync("https://myserver.ddns.net/" + scriptname, content);
var responseString = await response.Content.ReadAsStringAsync();
string SJson = responseString.ToString();
messagedialog.Content = SJson;
Exception log:
System.Net.Http.HttpRequestException
HResult=0x80072F0D
Message=An error occurred while sending the request.
Source=System.Net.Http
StackTrace:
at System.Net.Http.HttpClientHandler.d__86.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.Http.HttpClient.d__58.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at Aplikacija_iFE.MainPage.d__10.MoveNext() in D:\Onedrive\myproject\myproject\App\App\MainPage.xaml.cs:line 103
Inner Exception 1:
COMException: The text associated with this error code could not be found.
Overitelj digitalnih potrdil ni veljaven ali pa je napačen
The bold string is in my native language and tells me that the CA is invalid or wrong (Basically it is ,because I signed it myself). Can this error be fixed temporarily with some C# code or must I replace the certificate?
My HTTPS (Apache) server is on a Debian 9 machine.
Edit (10:20 PM): Working code
The following code works for now, but it is ugly, highly insecure, and just a shane for me as a student who's new to programming :|
string scriptname = "MyRestAPI.php";
HttpFormUrlEncodedContent content = new HttpFormUrlEncodedContent(values);
HttpResponseMessage response = new HttpResponseMessage();
try
{
client = new HttpClient();
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
catch (Exception e)
{
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
ChainValidationResult[] results = new ChainValidationResult []
{
ChainValidationResult.Untrusted, ChainValidationResult.WrongUsage,
ChainValidationResult.BasicConstraintsError, ChainValidationResult.Expired,
ChainValidationResult.IncompleteChain, ChainValidationResult.InvalidCertificateAuthorityPolicy,
ChainValidationResult.InvalidName, ChainValidationResult.OtherErrors,
ChainValidationResult.RevocationFailure, ChainValidationResult.RevocationInformationMissing,
ChainValidationResult.Revoked, ChainValidationResult.UnknownCriticalExtension
};
for(int i=0;i<results.Length;i++)
{
try
{
filter.IgnorableServerCertificateErrors.Add(results[i]);
client = new HttpClient(filter);
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
catch
{
continue;
}
}
client = new HttpClient(filter);
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
finally
{
client.Dispose();
}
messagedialog.Content = response.Content.ToString();
You can wither use a config to ignore this error in development environment or make your client to trust the certificate, i.e just add the certificate to your trusted root on your client.

Categories

Resources