Mario Vernari
Mario Vernari

Reputation: 7304

Async/await and WebException handling

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

Answers (1)

gitesh.tyagi
gitesh.tyagi

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.

  1. async void is of type fire & forget and is only & only for event handlers.
  2. As soon as compiler reaches first await operator inside async method control returns to the caller.

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

Related Questions