如何在异步HttpClient调用中捕获所有异常?

时间:2019-09-21 09:57:28

标签: c# .net .net-4.0 httpclient

我具有以下下载网页的功能:

    static bool myFunction(int nmsTimeout, out string strOutErrDesc)
    {
        //'nmsTimeout' = timeout in ms for connection
        //'strOutErrDesc' = receives error description as string
        bool bRes = false;
        strOutErrDesc = "";

        HttpClient httpClient = null;
        System.Threading.Tasks.Task<string> tsk = null;

        try
        {
            httpClient = new HttpClient();
            tsk = httpClient.GetStringAsync("https://website-to-connet.com");

            if (tsk.Wait(nmsTimeout))
            {
                if (tsk.Status == System.Threading.Tasks.TaskStatus.RanToCompletion)
                {
                    string strRes = tsk.Result;
                    strRes = strRes.Trim();

                    if (!string.IsNullOrWhiteSpace(strRes))
                    {
                        bRes = true;
                    }
                    else
                    {
                        //Empty result
                        strOutErrDesc = "Empty result";
                    }
                }
                else
                {
                    //Bad task completion
                    strOutErrDesc = "Bad completion result: " + tsk.Status.ToString();
                }
            }
            else
            {
                //Timed out
                strOutErrDesc = "Timeout expired: " + nmsTimeout + " ms.";
            }
        }
        catch (Exception ex)
        {
            //Error
            strOutErrDesc = "Exception: " + ex.Message;
            if (tsk != null)
            {
                strOutErrDesc += " -- ";
                int c = 1;
                foreach(var exc in tsk.Exception.InnerExceptions)
                {
                    strOutErrDesc += c.ToString() + ". " + exc.InnerException.Message;
                }
            }

            bRes = false;
        }

        return bRes;
    }

我认为我的try / catch构造足以捕获其中的所有异常。

直到我发现以下异常以及该应用程序崩溃的Windows错误消息:

enter image description here

  

未处理的异常:System.AggregateException:任务的异常   等待任务或访问任务未观察到   异常属性。结果,未观察到的异常被重新抛出   通过终结器线程。 ---> System.Net.Http.HttpRequestException:   响应状态代码不表示成功:503(服务   不可用)。

     

---内部异常堆栈跟踪结束---

     

在System.Threading.Tasks.TaskExceptionHolder.Finalize()

这是什么,我该如何捕捉?

2 个答案:

答案 0 :(得分:0)

该应用程序崩溃了,因为try / catch并非“随处捕获”黑客,它仅捕获在同一调用堆栈或同一调用上下文中引发的异常。

另一方面,由于某种原因,您使用同步方法来启动异步任务,这些任务在其他线程上运行,并且它们的上下文丢失。

或者使用这些方法的同步版本,或者更好的是,在异步任务上使用async方法和await,这将保留调用上下文并允许您捕获try /中从内部抛出的任何异常。捕获块。

答案 1 :(得分:0)

请注意,此处描述的是旧行为(如注释中所述),. NET 4.5及更高版本不会发生。

正在发生的情况是任务未成功完成,并且您没有检查错误。当垃圾收集器尝试清理Task对象时,它会在该对象中找到未处理的异常并将其扔到AggregateException中。这是一个例外,实际上并没有引发您的try块(它甚至在不同的线程上),因此您的catch无法捕获它。

您要执行的操作是正确await创建的任务。此时,您可能想在C#中阅读异步/等待。如果您希望任务可以取消,则可能必须将GetAsync与取消令牌一起使用,否则您将不得不等待GetStringAsync在某个时候完成。

如果由于某种原因(您应该!)不希望使用异步等待方式,则仍然可以使用tsk.Wait();。但是,这会将引发的异常包装在AggregateException中,并且调用将是同步的

如果您真的无法等待任务完成,则可以在this question中看到如何通过连续任务自动处理异常检查。

但是我真的建议您使用异步/等待并适当地检查任务如何完成以及它们抛出了什么。