HttpClient.PostAsync crash app. How to catch the Exception? - c#

I'm trying to test this HttpRequest:
public void TestX(string baseUrl)
{
StringContent httpContentDistanza = new StringContent(GlobalVariables.JsonDistanza);
using HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpResponseMessage responseMessage = null;
try
{
responseMessage = httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza).Result;
// can't reach the code below
if (responseMessage.IsSuccessStatusCode)
{
string strContext = responseMessage.Content.ReadAsStringAsync().Result;
var risultato = JsonSerializer.Deserialize<Distanza1>(strContext);
GlobalVariables.DblAijCrnPsz = risultato.data.processDataIn.valore;
}
}
catch (Exception ex)
{
if (responseMessage == null)
{
responseMessage = new HttpResponseMessage();
}
responseMessage.StatusCode = HttpStatusCode.InternalServerError;
responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
}
}
The problem is that the URI is not reachable, and I was expecting that its gonna throw some Exception, but it did not.
In case where URI is not reachable I need some how to catch that Exception.
I'm using BackgroundWorker to run TestX():
public MainWindow()
{
InitializeComponent();
bgWorker = new BackgroundWorker { WorkerReportsProgress = true };
bgWorker.DoWork += ResetAll;
}
private void ResetAll(object sender, DoWorkEventArgs e)
{
var x = gestLink.TestX(GlobalVariables.BaseUrl).ToString();
//it also does't reach the code below
....
}
Update
I don't know what I'm doing wrong.. I still can't catch the exception :
public async Task TestX(string baseUrl)
{
StringContent httpContentDistanza = new StringContent(GlobalVariables.JsonDistanza);
using HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpResponseMessage responseMessage = null;
try
{
responseMessage = await client.PostAsync("/xxx/xxx/xx", httpContentDistanza);
if (responseMessage.IsSuccessStatusCode)
{
string strContext = await responseMessage.Content.ReadAsStringAsync();
var risultato = JsonSerializer.Deserialize<Distanza1>(strContext);
}
}
catch(Exception ex)
{
var error = ex.Message;
}
It crash here responseMessage = await httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza); but stops in ResetAll() and var x = this.
I have seen and read similar problems (Debugger stops after async HttpClient.GetAsync() and HttpClient.PostAsync and await crashing app) before, but no solutions have helped me.
Any suggestions how to catch the exception?

You should change this:
public void TestX(string baseUrl)
to
public async Task TestX(string baseUrl)
and this
responseMessage = httpClient.PostAsync("/xxx/xx/xxx",httpContentDistanza).Result;
to
responseMessage = await httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza);
Then you can handle Exceptions.

Related

HttpClient with dynamic proxy

I have a method MultiLikeAsync(username, password, proxy) which can be executed multiple times and each time with different username, password, proxy or no proxy at all.
for (int i = 0; i < 15; ++i)
{
Class class = new Class();
await class.MultiLikeAsync(random_username, random_password, random_proxy);
}
This is my current setup:
public class Class
{
private static readonly HttpClient _httpClient;
static Class()
{
if (_httpClient == null)
{
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
_httpClient = new HttpClient(handler);
}
}
public async Task MultiLikeAsync(string username, string password, string proxy = null)
{
//_httpClient.Proxy = ??? // Can't do that
// first request
HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, "url");
// Polly
var timeoutPolicy = Policy.TimeoutAsync(1);
HttpResponseMessage httpResponse = await timeoutPolicy.ExecuteAsync(async ct => await _httpClient.SendAsync(httpRequest, ct), CancellationToken.None);
// second request
HttpRequestMessage httpRequest2 = new HttpRequestMessage(HttpMethod.Post, "different_url");
// Polly
HttpResponseMessage httpResponse2 = await timeoutPolicy.ExecuteAsync(async ct => await _httpClient.SendAsync(httpRequest2, ct), CancellationToken.None);
}
}
The problem with my solution is that I can't dynamically set _httpClient.Proxy for each MultiLikeAsync call. It has to be set along with HttpClientHandler during HttpClient instantiation or with multiple HttpClient instances.
It's a bad idea to reinstantiate HttpClient more than once, because it creates socket exhaustion and that's the reason they introduced IHttpClientFactory in .NET Core 2.1. Even if I used IHttpClientFactory, I would have had same problem, because it doesn't allow me to dynamically change proxies for each request. More info about it.
I need a solution immune to socket exhaustion (just like IHttpClientFactory) that allows me use a different proxy for each MultiLikeAsync call. Additionally, I want to keep my Polly Timeout.
Edit:
After your example given below, it's kinda the same. It doesn't close the TIME_WAIT requests. They are disposed after 240 seconds.
public class Class
{
private const int Timeout = 6;
public async Task MultiLikeAsync(string username, string password, string proxy = null)
{
using HttpClientHandler httpHandler = new HttpClientHandler();
if (proxy != null)
{
httpHandler.Proxy = new WebProxy(proxy, true);
}
if (httpHandler.SupportsAutomaticDecompression)
{
httpHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
using HttpClient httpClient = new HttpClient(httpHandler);
try
{
using HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, "url");
var timeoutPolicy = Policy.TimeoutAsync(Timeout);
HttpResponseMessage httpResponse = await timeoutPolicy.ExecuteAsync(async ct => await httpClient.SendAsync(httpRequest, ct), CancellationToken.None);
if (httpResponse.IsSuccessStatusCode)
{
string content = await httpResponse.Content.ReadAsStringAsync();
...
}
using HttpRequestMessage httpRequest2 = new HttpRequestMessage(HttpMethod.Post, "different_url");
HttpResponseMessage httpResponse2 = await timeoutPolicy.ExecuteAsync(async ct => await httpClient.SendAsync(httpRequest2, ct), CancellationToken.None);
if (httpResponse2.IsSuccessStatusCode)
{
string content = await httpResponse2.Content.ReadAsStringAsync();
...
}
}
catch (TimeoutRejectedException ex)
{
if (ex.InnerException is TaskCanceledException)
{
}
}
catch (HttpRequestException ex)
{
if (ex.InnerException is SocketException)
{
}
}
catch (Exception)
{
}
}
}

waiting for an exception from an Action

I have this function:
public static async Task<T> Relay<T>(string uriController, T jsonObject)
{
var json = string.Empty;
try
{
LogHelpers.LogVerbose( "Enter " + uriController );
using ( var client = new HttpClient() )
{
HttpResponseMessage response;
client.BaseAddress = Uri;
client.Timeout = new TimeSpan( 0, 0, 0, 20, 0 );
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) );
if ( jsonObject != null )
{
var jsonInput = JsonConvert.SerializeObject( jsonObject );
var contentPost = new StringContent( jsonInput, Encoding.UTF8, "application/json" );
response = await client.PostAsync( uriController, contentPost ).ConfigureAwait( false );
}
else
{
response = await client.PostAsync( uriController, null ).ConfigureAwait( false );
}
response.EnsureSuccessStatusCode();
if (response.StatusCode != HttpStatusCode.OK)
{
return default(T);
}
json = await response.Content.ReadAsStringAsync().ConfigureAwait( false );
LogHelpers.LogVerbose( "Exit " + uriController );
SendNotification?.Invoke();
}
}
catch (Exception e)
{
throw e;
}
return JsonConvert.DeserializeObject<T>(json);
}
and this is called by:
private async void GetPiSystemInfo()
{
SystemPiInfo = await RaspberryPi.Relay<SystemPiInfo>("Api/SystemPiInfo/IsConnected", null);
}
which in turn is run from this:
private async Task<bool> RunTheMethod(Action myMethodName)
{
await Task.Run(
() =>
{
while (true)
{
try
{
myMethodName();
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
});
return false;
}
Which finally is called from this:
Action[] Actions = new Action[10];
Actions[0] = GetPiSystemInfo;
await RunTheMethod(Actions[0]);
The reason for this architecture is that when my app starts it will connect to my Raspberry Pi box. Several api calls are made to get the data from that box.
If for any reason connection is not made or an error is returned I wish to rerun the action until it does so (obviously there would be a cut off).
Task<T> Relay<T>()
Method is generic as it will be called from many different places. So, whenever a server error occurs I would catch and throw back up the stack.
What is happening though is that
return true;
is being called as soon as the action is called.

HttpClient fails to initialize

After running with the debugger this line of code
HttpClient client = new HttpClient()
the application suddenly exits. Anybody have some ideas why this appears?
This is the code:
static void Main(string[] args)
{
Task.Run(() => translate("en", "bg", "hello").Wait());
}
static async Task translate(string sourceLang, string targetLang, string sourceText)
{
string page = "http://www.transltr.org/api/translate?text=" +
sourceText + "&to=" + targetLang + "&from=" + sourceLang;
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(page);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
string trans = await response.Content.ReadAsStringAsync();
Console.WriteLine(trans);
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
and it stops executing after this line
HttpClient client = new HttpClient()
Task.Run(() => translate("en", "bg", "hello")).Wait();
Code is working, with a small adjustment you need to await task created at Task.Run

UploadDataTaskAsync never returns

I'm trying to upload data to an external webservice, i'm using the WebClients UploadDataTaskAsync to do this asynchronously. but it never returns. I've tested this using the synchronous call and it will timeout eventually.
public Notification Notify(Notification notification)
{
var messageXml = MessageFactory.Create(notification);
var statusCode = SendWebRequestAsync(messageXml);
notification.ProcessedAttempts++;
if (statusCode.Result == HttpStatusCode.OK)
{
notification.Processed = true;
notification.DateProcessed = DateTime.UtcNow;
}
return notification;
}
private async Task<HttpStatusCode> SendWebRequestAsync(string message)
{
using (var webClient = new WebClient())
{
byte[] data = message.ToUtf8Bytes();
var uri = new Uri(url);
try
{
webClient.UploadDataCompleted += WebClient_UploadDataCompleted;
var result = await webClient.UploadDataTaskAsync(uri, "POST", data);
string response = Encoding.UTF8.GetString(result);
if (response == "")
return HttpStatusCode.OK;
return HttpStatusCode.NoContent;
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
return response.StatusCode;
}
}
// no http status code available
return HttpStatusCode.ServiceUnavailable; // guess
}
catch (Exception)
{
return HttpStatusCode.InternalServerError;
}
}
}
private void WebClient_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
{
Console.WriteLine(e.Result);
}
You're calling statusCode.Result which is the classic ASP.NET deadlock. This makes no sense because you're apparently using async IO to get scalability benefits and then destroying that by blocking.
Don't block.
Also, it looks like HttpClient could reduce the code that you have there a bit.

UploadStringAsync CallBack Return Type

I have a web service where I Post to a URL Asynchronously
public Response uploadXMLData(string destinationUrl, string requestXml,Request req)
{
try
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
using (WebClient client = new WebClient())
{
client.UploadStringCompleted
+= new UploadStringCompletedEventHandler(UploadStringCallback);
client.UploadStringAsync(uri, "POST",requestXml,req);
}
}
catch (Exception ex)
{}
return resp;
}
public void UploadStringCallback(object sender, UploadStringCompletedEventArgs e)
{
Response resp=new Response();
Request req = new Request();
try
{
if (e.Error != null)
{
object objException = e.Error.GetBaseException();
Type _type = typeof(WebException);
if (_type != null)
{
WebException objErr = (WebException)e.Error.GetBaseException();
WebResponse rsp = objErr.Response;
using (Stream respStream = rsp.GetResponseStream())
{
req= (Request)e.UserState;
resp=Utilities.ParseWithoutSoapEnv(respStream);
}
}
else
{
Exception objErr = (Exception)e.Error.GetBaseException();
throw objErr;
}
}
else
{
//Parse e.Result
}
}
catch (Exception ex)
{}
}
The Function Called Utilities.ParseWithoutSoapEnv(respStream); returns Type Response
what I want to do is get the Response from that function, and make it the return value for uploadXMLData
But I can't change the return type of a CallBack Function, so I have no idea what to do.
When someone calls my webservice function, they expect it to return a type of Response, and the Response class I need is being received to the CallBack function..
Hope I made my issue clear
Any help would be appreciated
Things get a little easier if you use the Task Asynchronous Pattern and HttpClient. You'll need to modify your method to return a Task<Response>:
public async Task<Response> UploadXmlDataAsync(
string destinationUrl,
string requestXml,
Request req)
{
try
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
var httpClient = new HttpClient();
var response = await httpClient.PostAsync(Uri, new StringContent(requestxml))
.ConfigureAwait(false);
var responeStream = response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return Utilities.ParseWithoutSoapEnv(responseStream);
}
}
And when you call it higher up the call-stack, you'll need to await that too:
public async Task FooAsync()
{
var parsedResponse = await UploadXmlDataAsync(url, xml, request);
// Do something with response.
}
If you want a synchronous alternative, you can also do:
public Response UploadXmlData(string destinationUrl, string requestXml,Request req)
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
using (WebClient client = new WebClient())
{
var response = client.UploadString(uri, "POST", requestXml, req);
using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(response))
{
return Utilities.ParseWithoutSoapEnv(responseStream);
}
}
}

Categories