きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × Angularなどの忘備録

C# × WebAPIの呼び出し方の学習メモ × リクエストの処理結果の判定方法について

目的

WebAPIの呼び出し方の学習メモ。
今回は、active-directory-dotnetcore-daemon-v2ソースコードを教材とした。

内容確認

全体像。

public async Task CallWebApiAndProcessResultASync(string webApiUrl, string accessToken, Action<IEnumerable<JObject>> processResult)
{
    if (!string.IsNullOrEmpty(accessToken))
    {
        var defaultRequestHeaders = HttpClient.DefaultRequestHeaders;
        if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
        {
            HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }
        defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        HttpResponseMessage response = await HttpClient.GetAsync(webApiUrl);
        if (response.IsSuccessStatusCode)
        {
            string json = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<List<JObject>>(json);
            Console.ForegroundColor = ConsoleColor.Gray;
            processResult(result);
        }
        else
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
            string content = await response.Content.ReadAsStringAsync();
            // Note that if you got reponse.Code == 403 and reponse.content.code == "Authorization_RequestDenied"
            // this is because the tenant admin as not granted consent for the application to call the Web API
            Console.WriteLine($"Content: {content}");
        }
        Console.ResetColor();
    }
}

認証情報の確認

if (!string.IsNullOrEmpty(accessToken)) {}

今回は、bearerタイプのアクセストークンを利用するWeb APIが対象となっている。まずは、呼び出し元から設定されたアクセストークンに値が入っているか確認する。値がない場合は、そのまま処理を終了する。今回の例は、コンソールアプリケーションのサンプルアプリであるため、呼び出し元にWeb APIの呼び出し結果を戻したりする必要がないため、特に何も処理せずに終了している。

Request headerの設定

var defaultRequestHeaders = HttpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
    HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

アクセストークンが設定されている場合、リクエストの作成を行う。今回は、受信可能なレスポンスデータのTypeとしてJsonを使うため、AcceptヘッダにJsonのMedia Typeを指定している。その前の条件式で、すでにJsonが指定されている場合のことが考慮されているため、重複して設定される心配がない。
Headerには、他にAuthenticationを設定する。このHeader情報のValueには、呼び出し元から渡されたアクセストークンを指定している。

Http Requestの実行

HttpResponseMessage response = await HttpClient.GetAsync(webApiUrl);

HTTP ClientにHeaderの設定を追加し、Getメソッドを呼び出している。HttpClientは、このクラスのプロパティとして設定されており、そのプロパティはコンストラクタで初期化されるようになっている。Dependency Injectionをする際は、IHttpClientFactoryを利用するほうがUnitTestが実装しやすいため良いと思うが、サンプルアプリなので、そこのあたりはどうでもよいのかもしれない。今回はDI使っていないし。そのため、今回は、呼び出し元のメソッドで、このクラスをNewする前に、HttpClientをNewしてそれを渡している形となっている。

結果の判定(本題はここ)

if (response.IsSuccessStatusCode)
{
    string json = await response.Content.ReadAsStringAsync();
    var result = JsonConvert.DeserializeObject<List<JObject>>(json);
    Console.ForegroundColor = ConsoleColor.Gray;
    processResult(result);
}
else
{
    Console.ForegroundColor = ConsoleColor.Red;
    Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
    string content = await response.Content.ReadAsStringAsync();
    // Note that if you got reponse.Code == 403 and reponse.content.code == "Authorization_RequestDenied"
    // this is because the tenant admin as not granted consent for the application to call the Web API
    Console.WriteLine($"Content: {content}");
}

HttpResponseMessageを受け取り、内容の判定を行い、結果をコンソールに出力している。
Httpリクエストの結果判定には、まず、IsSuccessStatusCodeプロパティを利用している。これは知らなかった。このプロパティは、true if System.Net.Http.HttpResponseMessage.StatusCode was in the range 200-299;otherwise, false.という仕様のもと、結果を判定できるようになっている。基本的に200:okを判定することが多いため、これで事足りるケースが多いはず。
しかし、よく見ると、200-299の範囲のステータスコードの時のみtrue判定となることがわかる。例えば、何かの情報を検索した際、検索条件に一致するアイテムが0件の場合にNot Foundを返す仕様のAPIが存在する。検索結果が0件でも、正常とみなすケースでは、このプロパティを利用すると重複した実装をTrue/Falseステートメントの両方に実装しなくてはならなくなる。
何をもってSuccessとするかは、呼び出すWeb APIの仕様と、クライアント側の判定仕様を合わせて考える必要がありそう。
そうなると、やっぱり、HttpStatusCodeをみて判断するという考えを第一とするのがセーフティーなのかもしれない。

参考