Reputation: 7304
I am struggling around the (seems so) pretty famous problem of the exception handling by using the async/await pattern. Specifically my context is on a HTTP client, but I have also tried with a much simpler test, and it behaves the same.
Consider the below program, which is a super-simplified version of my original app's context.
class Program
{
static void Main(string[] args)
{
Test();
Console.Write("Press any key...");
Console.ReadKey();
Console.WriteLine();
}
static async void Test()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
}
}
class MyClient
{
public async Task<string> GetString(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
When the program starts, it downloads the first web site's page as a string, then displays its length: that's fine. Afterward, when the same operation is performed against an invalid address, the client raises a WebException (that's what I want), but it's not caught.
UPDATE: as "not caught", I mean that the code actually does not flow through the "catch" branch and silently displays the exception message. Instead, the exception is shown by the VS IDE, and the debugging breaks.
Any decent solution to catch the exception?
Many thanks in advance.
Upvotes: 0
Views: 493
Reputation: 2381
Although you have already figured out the exception is HttpRequestException
not WebException
, still I would like to highlight few important things about async-await operator usage.
async void
is of type fire & forget and is only & only for event handlers.Debugging your code :-
Since you are using async void in Test method so the control returns to the caller and execution continues to line Console.Write("Press any key...");
without having any information about the Task and then you are waiting for the user input.
In the meanwhile response from awaited method comes and the execution continues inside Test method.
If you comment out the line Console.ReadKey();
inside main() OR user provides input immediately then you'll notice that response may or may not get printed. This is because you are not waiting on the Task getting executed you simply trusted on the user that he will not enter anything till your Task completes.
Solution:-
Solution is to return Task from Test() and then wait till it finishes, below is the updated code also note adding Async at the end of method name is the naming convention you must follow to save you from the headache of distinguishing between asynchronous and synchronous methods.
class Program
{
static void Main(string[] args)
{
Task task = TestAsync();
Console.Write("Press any key...");
task.wait();
//Console.ReadKey();
Console.WriteLine();
}
static async Task<string> TestAsync()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
//to avoid compiler error
return null;
}
}
class MyClient
{
public async Task<string> GetStringAsync(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
Upvotes: 1