尝试捕获异步等待中的异常。

4
我遇到了一个非常奇怪的情况。我的一些异步/等待代码使用RestSharp从几个rest API(托管在不同URL上的相同API)中获取一些数据,这是一个目录服务返回的API。
现在问题来了:由该目录服务返回的其中一个API是某种“私有”的,尝试建立SSL连接失败。Fiddler捕获以下响应:
HTTP/1.1 200 Connection Established
FiddlerGateway: Direct
StartTime: 19:13:11.117
Connection: close

fiddler.network.https> HTTPS handshake to foo.bar.com failed. System.IO.IOException The handshake failed due to an unexpected packet format.

我希望在这种情况下能够跳过从此API获取数据并继续执行。然而,尝试使用try / catch语句并没有起到帮助作用!会抛出NullReferenceException异常,即使是通用的try / catch也无法捕获它。
代码非常简单:
try
{
   await GetDataAsync(url);
}
catch 
{
   // never gets called
}

并且。
private async Task<List<Data>> GetDataAsync(string url)
{
   var request = new RestRequest("/foo");
   var restClient = new RestClient(url);

   var response = await restClient.ExecuteTaskAsync<List<Data>>(request); // <-- this throws 
   return response.Data;
}

我已将代码提取到一个库中,并尝试在控制台应用程序和WPF应用程序中运行,结果相同,catch块从未被执行。有什么想法吗?附上完整的异常堆栈跟踪如下:
Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
at RestSharp.RestClient.<>c__DisplayClass15`1.<ExecuteTaskAsync>b__12(IRestResponse`1 response, RestRequestAsyncHandle _)
at RestSharp.RestClient.DeserializeResponse[T](IRestRequest request, Action`2 callback, IRestResponse response, RestRequestAsyncHandle asyncHandle)
at RestSharp.RestClient.<>c__DisplayClassa`1.<ExecuteAsync>b__9(IRestResponse response, RestRequestAsyncHandle asyncHandle)
at RestSharp.RestClient.ProcessResponse(IRestRequest request, HttpResponse httpResponse, RestRequestAsyncHandle asyncHandle, Action`2 callback)
at RestSharp.RestClient.<>c__DisplayClass3.<ExecuteAsync>b__0(HttpResponse r)
at RestSharp.Http.ExecuteCallback(HttpResponse response, Action`1 callback)
at RestSharp.Http.ResponseCallback(IAsyncResult result, Action`1 callback)
at RestSharp.Http.<>c__DisplayClass3.<GetStyleMethodInternalAsync>b__1(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.HttpWebRequest.SetResponse(Exception E)
at System.Net.HttpWebRequest.CheckWriteSideResponseProcessing()
at System.Net.ConnectStream.ProcessWriteCallDone(ConnectionReturnResult returnResult)
at System.Net.HttpWebRequest.WriteCallDone(ConnectStream stream, ConnectionReturnResult returnResult)
at System.Net.ConnectStream.CallDone(ConnectionReturnResult returnResult)
at System.Net.ConnectStream.IOError(Exception exception, Boolean willThrow)
at System.Net.ConnectStream.HandleWriteHeadersException(Exception e, WebExceptionStatus error)
at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.TlsStream.ResumeIOWorker(Object result)
at System.Net.TlsStream.WakeupPendingIO(IAsyncResult ar)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Security.SslState.FinishHandshake(Exception e, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
at System.Net.AsyncProtocolRequest.CompleteRequest(Int32 result)
at System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32 bytes)
at System.Net.FixedSizeReader.ReadCallback(IAsyncResult transportResult)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

return response.Data; 处设置一个断点,查看 response 的内容。 - L.B
我无法到达那一行,它在ExecuteTaskAsync上抛出异常。我编辑了问题以显示抛出异常的行。 - adrian h.
你能添加异常的堆栈跟踪吗? - i3arnon
完成,已编辑原帖 - adrian h.
如果这个异常没有被那个try-catch捕获,它在哪里被捕获了? - Paulo Morgado
显示剩余4条评论
1个回答

2
这似乎是RestSharp中的一个错误。我不确定你能否在不修改RestSharp源代码的情况下解决它。

你说得对。我也发现了 https://github.com/restsharp/RestSharp/issues/447,这基本上是相同的问题。然而,当我从GitHub获取最新的RestSharp代码以了解为什么异常会导致AppDomain崩溃时,我发现它现在已经修复了!我逐步检查了反序列化代码,现在它正确地检查是否有异常,并不再尝试反序列化响应体。 - adrian h.
我现在检出了一个较旧的RestSharp提交版本,其中仍存在错误,以尝试弄清楚为什么异常会导致应用程序崩溃。不是很确定,但据我所知,似乎RestSharp中的异步方法正在包装WebRequest上的非异步方法(例如BeginWebRequest)。我猜测,在这种情况下抛出的异常不受编译器重写异步/等待代码的支持?(并且要明确的是,当前版本的RestSharp4针对.net4进行编译,仍然不使用GetResponseAsync(),但由于实际错误已经修复,因此不再抛出NRE) - adrian h.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接