Sending a POST request with retry using HTTPClient - c#

So I've been searching like a mad man after a way to solve this issue, but I can't seem to find an answer.
So, I need to send a POST request with HTTPClient in C# to a server, and if the server isn't running it will keep sending the request until it connects (or dies after a nr of attempts). But I always get the exception System.Net.Http.HttpRequestException, which wouldn't be a problem if I just could store it (or something) and try again.
I found a couple of ways that people tried to do this, and I've tried them all. Creating a for-loop that loops and catches the exception that the program throws, adds to the counter and tries again. Creating a while-loop that loops until the HttpResponseMessage.IsSuccessStatusCode == true. I've even gone to such lengts as restarting the program if it can't connect (yeah, I'm that desperate).
So, I had to see if anyone of you guys might have a solution to this problem, or if you maybe had a better way to solve this problem.
Here is the code im running, thanks for your help!
EDIT: Just to add some clarity, the exception is thrown at the "rep"-variable, and the code never runs further than to that variable. And I've tried to make the HTTPResponseMessage variable just a "var" and await the Postasync method to.
HttpResponseMessage rep = new HttpResponseMessage();
try
{
rep = client.PostAsync("https://localhost:9999/", content).Result;
}
catch (Exception e)
{
}
Task t1 = Task.Factory.StartNew(() => ContinueTrasmission(client, c1.name, c1.state));

You have to look for retry libraries, for example Polly
var policy = Policy
.Handle<HttpRequestException>()
.WaitAndRetry(_retryCount, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
policy.Execute(() => DoSomething());

A solution could be:
bool success = false;
while (!success)
{
var rep = new HttpResponseMessage();
try
{
rep = client.PostAsync("https://localhost:9999/", content).Result;
//No exception here. Check your condition and set success = true if satisfied.
}
catch (Exception e)
{
//Log your exception if needed
}
}

Related

I am not getting any output and i don't know why

using System;
using System.Text;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
YourClient client = new YourClient();
client.Put();
}
public class YourClient
{
private readonly HttpClient _client;
public YourClient()
{
_client = new HttpClient();
}
public async Task Put() // must be async
{
using (var request = new HttpRequestMessage(HttpMethod.Put, "https://api.minecraftservices.com/minecraft/profile/name/egg"))
{
request.Headers.Add("Authorization", "Bearer token");
request.Content = new StringContent("body", Encoding.UTF8, "content-type");
using (var response = await _client.SendAsync(request))
{
var data = await response.Content.ReadAsStringAsync();
var code = response.StatusCode;
Console.WriteLine(Convert.ToString(code));
// do something with data
}
}
}
}
}
}
I'm not getting any output and I don't know why. I'm trying to print the response code of the request but nothing is output, is it to do with my method?
I have tried printing hi after Client.Put() and it was printed, so I know that my code is actually running, I just don't know why it isn't printing the status code ...
The excellent comment by Prolog points out one of two issues. If your Console app is built on < C# 7.1 you will need a workaround to prevent the app from exiting (before the request has time to process) so in this case add Console.ReadKey() as the very last line. This will spin the message loop until you hit a key. But this is not the main issue and I would like to offer a couple of debugging tips.
The big issue is this:
If I run your code, your http request is failing and is throwing a System.FormatException
Usually this type of exception is not set to Break when Thrown. (You can verify this by looking in the Exception Settings window.) Unfortunately, this is giving you a silent failure in this case, so you must take matters into your own hands to observe it.
Suggestions for debugging your code
Use a try-catch block around any code that has any likelihood of failing.
Use System.Diagnostics.Debug.Assert which will cause your program to break on a line if any condition expression evaluates to false (but only when you're running in Debug mode not Release mode).
Add output statements to trace execution. Using Debug.WriteLine will send messages to the Output window (but again, only in Debug mode). Alternatively, since we have a Console app here, I'm using the main app window to output trace statements.
Example using 1-3:
public async Task Put() // must be async
{
Console.WriteLine("Begin Put()");
try
{
using (var request = new HttpRequestMessage(HttpMethod.Put, "https://api.minecraftservices.com/minecraft/profile/name/egg"))
{
request.Headers.Add("Authorization", "Bearer token");
request.Content = new StringContent("body", Encoding.UTF8, "content-type");
using (var response = await _client.SendAsync(request))
{
var data = await response.Content.ReadAsStringAsync();
var code = response.StatusCode;
Console.WriteLine(Convert.ToString(code));
// do something with data
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Assert(condition: false, message: ex.Message);
}
Console.WriteLine("End Put()");
}
Now, if I run the code it will break and show what the problem is.
Use the Exception Settings window to turn on all exceptions (if in doubt). Now the code will break on the exact line that is the problem.
Verify that you are Setting Authorization Header of HttpClient correctly as this may be part of the root cause of the exception.
Finally, if you continue after the Debug.Assert you will see the following text in your console which will confirm whether your Put method has had a chance to complete or not.
Hope these suggestions help you solve this problem and future ones!
// This workaround for C# versions below 7.1 attempts to
// mimic an `async Main` method.
static void Main(string[] args)
{
RunAsync();
Console.ReadKey();
}
private static async void RunAsync()
{
YourClient client = new YourClient();
// Put() should be awaited inside an async method
await client.Put();
}

Workaround NotFound using Microsoft Graph API?

If someone doesn't have a profile picture, a NotFound exception kicks me out of the code. How can I work around this?
My Method:
if(graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync().IsNull())
{
Debug.Log("profilepicture not found");
}
else
{
var photo = await graphManager.Client.Users[mitarbeiter.Id].Photo.Content.Request().GetAsync();`
}
Response: It skips the if cause it thinks it's not null
What I need is a Method to create a Debug.Log if the exception happens and not throw me out
It seems that getting an exception when a photo is not found is a valid case. See docs on errors.
Try to handle it in the following way:
try
{
Stream photoContent = await graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync();
}
catch (ServiceException ex) when (ex.IsMatch(GraphErrorCode.ItemNotFound.ToString()))
{
Debug.Log("profilepicture not found");
}
P.S. I'm not sure precisely which GraphErrorCode should be used here.
I don't know the library you are using but is it possible that in the moment you do
graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync().IsNull()
what happens is you are basically doing
var asyncResult = graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync();
if(asyncResult.IsNull())
which of course would be true since you didn't give it any time to actually perform the request and finish.
It should probably rather be
var photo = await graphManager.Client.Users[mitarbeiter.Id].Photo.Content.Request().GetAsync();
if(photo.IsNull() )
{
Debug.Log("profilepicture not found");
}
else
{
...
}

Handling exceptions in xamarin

I've encountered a few dificulties trying to handle some errors in the best way possible. One of my cases for example is NullReferenceException.
To be more clear let me explain it in a few words. When I make a call to a server to receive some pieces of information, in some cases the server might have some problems and it will return of course null.
What I've done is I display an alert to let the user know that he can try later again. After this I try to send him in the previous page for example. And after all this my app still crushes.
What I'd like to do is to simply display the alert and then let the user stay in the same page without the application crushing.
These are a few pieces of my code:
tasks.cs
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
await Application.Current.MainPage.Navigation.PopAsync(); //actually I would like to stay in the same page
}
}
return ideas;
}
view.xaml.cs
private async void Button_Clicked(object sender, EventArgs e)
{
Tasks ts = new Tasks();
var ideas = await ts.GetIdeasAsync();
if (ideas == null)
{
Debug.WriteLine("hello");
//do nothing since the display alert is already shown
}
else
{
//code here
}
I would really appreciate if anyone can guide me to a "best-practice" approach. Thanks :)
You are declaring ideas in the try block, and then trying to access it in the catch block, where it is out of scope. (Visual Studio should give an Intellisense error)
Also, whenever manipulating the UI, you should always do it on the main thread. so move your DisplayAlert() code into
Device.BeginInvokeOnMainThread(async () =>
{
// await DisplayAlert(); move it into here
});
In addition, any PopAsync or PushAsync calls should also be done on the main UI thread. But calling PopAsync after an asynchronous call to an API not be a good idea, as the user may have already pressed the back button by the time the call returns.
As for the NullReferenceException, check to see if json is null before passing it to the DeserializeObject() function.
The problem it was pretty obvious actually since I after catch-ing the exception I would later continue with the code. So what I did was :
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
//actually I would like to stay in the same page
return null; //-- added this line
}
}
return ideas;
}
Maybe it's not the best idea but it's working for me. Any other approach would be highly appreciated. :)

Visual Studio breakpoint inside HttpClient.GetAsync call not getting hit

I'm trying to debug an asynchronous call from a test script inside my .NET webservice, but the breakpoints inside my async call are never getting hit. I even tried putting a Debugger.Break() inside of it. Below is the calling code...
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["BaseAddress"]);
string uri = "/api/Rd_Regions";
// Below is the line of code I want to step into, but it won't step into the 'client.GetAsync(uri)'...
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
// Convert the result into business object
// Do stuff...
}
else do other stuff...
and the part of the webservice that should be getting called where the breakpoints are is here, the first is the context of the web api, followed by the method being called. I'd be happy if it stopped in either...
public partial class PIMSContext : DbContext
{
public PIMSContext()
: base(new OracleConnection(Security.ConfigurationReader.GetAppSetting("PIMS")), true)
//: base(new OracleConnection(ConfigurationManager.ConnectionStrings["PIMS"].ConnectionString), true)
etc....
And here is the method that is ultimately called:
// GET: api/RD_REGIONS
public IQueryable<RD_REGIONS> GetRD_REGIONS()
{
// I want the debugger to stop here!
Debugger.Break();
return db.RD_REGIONS;
}
Am I missing something? Is it not possible to step into this asynchronous call?
Any insight is appreciated.
Forgot to update with the answer earlier - it turns out I was accidentally debugging in Release mode (VS2015). Switching to Debug mode fixed it - all breakpoints started behaving as expected.
If i have understood you correctly - all breakpoints listed above are not getting hit? Not in await client.GetAsync(uri); neither in web-service GetRD_REGIONS() ? Is the code following after client.GetAsync not reached?
If it's true - maybe this method is never completed?
await client.GetAsync(uri);
Than it's possible that you are getting an exception in this place. Try to surround your GetAsync method with try/catch and place the breakpoint inside the catch block. Something like this:
...
HttpResponseMessage response = null;
try {
response = await client.GetAsync(uri);
}
catch (Exception e) {
throw e; //breakpoint goes here
}
...
Sometimes, because of your method is asynchronous, unhandled exceptions can't be registered globally by debugger or event-log. You can get this exception only with try/catch.

Xamarin Android Questions and Issues when working with WebServices (SOAP .NET 2.0)

I've been working with Xamarin and Webservices (Stated as .NET 2.0 Webservice in Xamarin) in the past few weeks. And I've runned into a few problems that I couldn't figure a workaround yet.
1st) How do I set a timeout for the webservice? The Timeout property is ignored regardless of its value. I believe the default being used is something around 30 seconds. But I'd like 5 seconds~~.
2nd) When the WebService call any method async, using the Begin. Is there anyway to check whether the async method was completed correctly or was timed out? The only way I got to "check" is by perfoming a try/catch in the End method. If it was fired because of a timeout, it will raise an exception. But I wonder if there is some property or method somewhere that would tell me if the webservice call did timeout or was processed correctly. The webservice method being used here is "IsAlive".
WebService.MainService.BeginIsAlive ((ar) =>
{
try
{
bool result = WebService.MainService.EndIsAlive (ar); //If timedout will raise an exception.
RunOnUiThread (() =>
{
Toast.MakeText (this, "Running as expected...", ToastLength.Long).Show();
});
StartActivity (typeof(OtherScreen));
}
catch (Exception ex)
{
//Probably timeout.
}
}, null);
3rd) Is there a way to Cancel a webservice async operation? Such as "CancelIsAlive". The only method I found was Webservice.Abort. But I do not believe it is a best practice and since it is not specific, it may screw everything up.
Thanks in advance, Luís Henrique.
That's what usually I do for calling WebServices and having control on them (assume your ws function needs an string and returns an string):
public static void CallSomeFunction(string SomeParameter, Action<string> Ok, Action Error, Activity Context)
{
ThreadPool.QueueUserWorkItem ((object e) => {
var proxy = new YourProxyClass();
proxy.Timeout = 10000;
try{
var res = proxy.YourFunction(SomeParameter);
Context.RunOnUiThread(() => Ok(res));
}
catch(Exception Ex){
if(Error != null)
Context.RunOnUiThread(Error);
}
});
}
This is how I do asynchronous tasks, use the ThreadPool to make synchronous calls, and pass some Actions to execute if all is ok or wrong. Also I'm passing a Context so my Ok and Error actions be executed in the UI thread.

Categories

Resources