当基本身份验证使用无效凭据时,Windows.Web.Http.HttpClient#GetAsync会引发不完整的异常

Jer*_*vel 21 .net c# async-await windows-runtime dotnet-httpclient

我正在使用Windows运行时组件进行API调用.直到今天早些时候,我使用了HttpClient相关的模型,System.Net但转而Windows.Web使用WinRT流.

除了将using报表,交换HttpContentIHttpContent并使用WindowsRuntimeExtensions改变我IInputStreamStream为JSON.NET,我没有做什么特别的事情.然而,在我的16次测试中突然有3次失败,而之前一切都运转了

所有3个(集成)测试都验证我在使用无效凭据登录时收到错误响应.还有其他测试包括登录(但使用有效的凭据),它们工作得很好.给定的错误消息属于类型AggregateException并具有消息

System.AggregateException:发生一个或多个错误.---> System.Exception:找不到元素.

无法显示对话框,因为尚未设置父窗口句柄.

该异常包含HRESULT值.outerexception具有-2146233088与对应0x80131500的innerexception -2147023728对应的值0x80070490.这些都不是MSDN页面已知错误代码.

经过调查:

堆栈跟踪:

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
Run Code Online (Sandbox Code Playgroud)

最初我的问题措辞有所不同,因为实际问题似乎是隐藏的.我发现GET请求HttpClient返回给调用者而不是等待调用的结果(并执行方法的其余部分).

在我的项目中,执行该行将var data = await myHttpClient.GetAsync(url);返回带有非构造对象的调用方法,并且在GetAsync()调用之后的后续行不会被执行.

添加.ConfigureAwait(false)以阻止它返回没有任何区别.

AggregateException当用户试图用无效凭证登录异常.由于某种原因,HttpClient决定抛出一个异常然后没有给我一个我可以使用的返回值.这里的问题是,它并没有告诉我什么样的异常:捕捉COMException,TaskCanceledException,AggregateExceptionException只会触发了后者.

我还发现异步集成测试不适用于多线程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;
   }
}
Run Code Online (Sandbox Code Playgroud)

使用有效密码执行此代码将返回,true而无效密码将抛出AggregateException.

现在我正在通过Exception围绕调用捕获一个将军来解决这个问题,GetAsync()但这是非常简陋的,我想知道为什么这个不完整的异常首先被抛出.

Yuv*_*kov 30

重建你的例子并玩弄后,我想出了会发生什么.

var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

添加HttpBaseProtocolFilter后的响应

  • 我很感激彻底的帮助!你是完全正确的,这解决了它.我会把赏金开放一段时间,所以会有更多的意见给你答案,但请放心,你会得到它. (5认同)