web-dev-qa-db-ja.com

Windows.Web.Http.HttpClient#GetAsyncは、基本認証で無効な資格情報が使用されると、不完全な例外をスローします

私は、API呼び出しを行うWindowsランタイムコンポーネントに取り組んでいます。今日まで、私はHttpClientと_System.Net_の関連モデルを使用しましたが、WinRTストリームを利用する代わりに_Windows.Web_に切り替えました。

usingステートメントを変更すること、HttpContentIHttpContentにスワップすること、およびWindowsRuntimeExtensionsを使用してJSON.NETのIInputStreamStreamに変更すること以外に、特別なことをする必要はありませんでした。しかし、突然、私の16テストのうち3テストが失敗しました。

3(統合)テストはすべて、無効な資格情報でログインしたときにエラー応答が返されることを検証します。 (有効な資格情報を使用した)ログインも含む他のテストがあり、それらは正常に動作します。指定されたエラーメッセージはAggregateExceptionタイプであり、メッセージとして

_System.AggregateException_:1つ以上のエラーが発生しました。 ---> _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
_

もともと実際の問題は隠されているようだったので、私の質問は少し違った言い方をされました。 HttpClientによるGETリクエストが、呼び出しの結果を待つ(およびメソッドの残りの部分を実行する)代わりに、呼び出し元に戻ることがわかりました。

私のプロジェクトでは、var data = await myHttpClient.GetAsync(url);行を実行すると、構築されていないオブジェクトを含む呼び出し元のメソッドに戻り、GetAsync()呼び出しの後に続く行は実行されません。

.ConfigureAwait(false)を追加して元に戻らないようにしても、違いはありませんでした。

AggregateExceptionは、ユーザーが無効な認証情報でログインしようとするとスローされます。何らかの理由で、HttpClientは、使用できる戻り値を返さずに例外をスローすることにしました。ここでの問題は、それがどのような例外を通知しないかです:COMExceptionTaskCanceledExceptionAggregateException、および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 = "[email protected]: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がスローされます。

現在、私はGetAsync()の呼び出しに関する一般的なExceptionをキャッチすることで問題を回避していますが、これは非常に初歩的なものであり、この不完全な例外が最初にスローされる理由を知りたいです。

21
Jeroen Vannevel

例を再構築して遊んだ後、私は何が起こるかを理解しました。

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 = "[email protected]: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;
}

Response after adding HttpBaseProtocolFilter

30
Yuval Itzchakov

AllowUIHttpBaseProtocolFilterfalseに設定すると、このエラーが停止します。

ただし、ユーザーが資格情報を入力できるようにダイアログボックスを表示したい場合は、UIスレッドでWeb要求を開始する必要があります。

1

問題は、EnsureSuccessStatusCodeの呼び出しによって実際に例外がスローされることだと思います。

HttpResponseMessageクラスのEnsureSuccessStatusCode()メソッドを追加して確認しましたか?.

http://msdn.Microsoft.com/en-us/library/system.net.http.httpresponsemessage.ensuresuccessstatuscode.aspx

この行の後:

var data = await client.GetAsync(new Uri("https://mytestdomain/v2/apikey?format=Json"));
date.EnsureSuccessStatusCode();
0
Joshua I