我正在开发一个可以进行API调用的Windows运行时组件。直到今天早些时候,我使用HttpClient
中的System.Net
及相关模型,但转而使用Windows.Web
来利用WinRT流。
除了更改using
语句,将HttpContent
交换为IHttpContent
并使用WindowsRuntimeExtensions
将我的IInputStream
更改为Stream
以获取JSON .NET,我没有做任何特别的事情。然而,在我的16次测试中突然有3次失败,而之前的一切都有效。
所有3个(集成)测试验证我在使用无效凭据登录时收到错误响应。还有其他测试包括登录(但使用有效的凭据),它们工作得很好。给定的错误消息类型为AggregateException
,并具有消息
System.AggregateException
:发生了一个或多个错误。 --->System.Exception
:找不到元素。由于尚未设置父窗口句柄,因此无法显示对话框。
该异常包含HRESULT值。 outerexception的值-2146233088
对应0x80131500
,而innerexception的-2147023728
对应0x80070490
。这些都不是the MSDN page上的已知错误代码。
经过调查:
0x80131500
corresponds to COR_E_EXCEPTION
0x80070490
corresponds to ERROR_NOT_FOUND
堆栈跟踪:
Result StackTrace:
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at xx.Models.Requests.GetRequest.<ExecuteRequestAsync>d__0.MoveNext() in c:\Users\jeroen\Github\Windows-app\xx\xx\Models\Requests\Request.cs:line 17
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at xx.ApiDispatcher.<ExecuteAsync>d__0`2.MoveNext() in c:\Users\jeroen\Github\Windows-app\xx\xx\ApiDispatcher.cs:line 40
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at xx.ApiDispatcher.Execute[TCallResult,TResponseObject](ApiCall`2 call) in c:\Users\jeroen\Github\Windows-app\xx\xx\ApiDispatcher.cs:line 22
最初我的问题措辞有所不同,因为实际问题似乎是隐藏的。我发现HttpClient
的GET请求返回给调用者而不是等待调用的结果(并执行方法的其余部分)。
在我的项目中,执行行var data = await myHttpClient.GetAsync(url);
将返回带有非构造对象的调用方法,并且在GetAsync()
调用之后的后续行不会被执行。
添加.ConfigureAwait(false)
以阻止其返回没有任何区别。
当用户尝试使用无效凭据登录时,会引发AggregateException
。由于某种原因,HttpClient
决定抛出一个异常然后没有给我一个我可以使用的返回值。这里的问题是它没有告诉我什么样的例外:捕获COMException
,TaskCanceledException
,AggregateException
和Exception
只会触发后者。
我还发现异步集成测试在多线程MSTest环境中效果不佳,因此我解释了其他几个失败的测试(但是单独工作得很好)
最后,我还有一个演示问题的示例(但我无法提供基本身份验证的Web服务)!
[TestMethod]
public void TestMethod3()
{
Assert.IsTrue(new Test().Do().AsTask().Result);
}
public sealed class Test
{
public IAsyncOperation<bool> Do()
{
return DoSomething().AsAsyncOperation();
}
private async Task<bool> DoSomething()
{
var client = new HttpClient();
var info = "jeroen.vannevel@something.com:nopass";
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(info));
client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Basic", token);
var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
return true;
}
}
使用有效密码执行此代码将返回true
,而无效密码将返回AggregateException
。
现在我正在通过在Exception
的调用周围捕捉一般GetAsync()
来解决这个问题,但这非常简陋,我想知道为什么在第一次抛出这个不完整的异常地点。
答案 0 :(得分:30)
重建你的例子并玩弄后,我想出了会发生什么。
var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
GetAsync
方法使用无效凭据调用HTTP请求。发生的情况是返回的请求试图寻找一个窗口,您可以在其中输入正确的凭据,但找不到。因此,它会在搜索该窗口时抛出Element Not Found
。
可以通过创建HttpBaseProtocolFilter
并将AllowUI
属性设置为false
然后将其传递给HttpClient
来解决此问题:
private async Task<bool> DoSomething()
{
var httpBaseFilter = new HttpBaseProtocolFilter
{
AllowUI = false
};
var client = new HttpClient(httpBaseFilter);
var info = "jeroen.vannevel@something.com:nopass";
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(info));
client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Basic", token);
var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
return true;
}
答案 1 :(得分:1)
在AllowUI
上设置HttpBaseProtocolFilter
到false
将停止此错误。
但是,如果您确实需要显示一个对话框,允许用户输入凭据,则需要在UI线程上启动Web请求。
答案 2 :(得分:0)
我认为问题在于您调用EnsureSuccessStatusCode实际上引发了异常。
您是否尝试添加HttpResponseMessage类EnsureSuccessStatusCode()方法并检查。
这一行之后:
var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
date.EnsureSuccessStatusCode();