Httpクライアント

重要な API

HttpClient と、Windows.Web.Http 名前空間 API の残りを使用して、HTTP 2.0 および HTTP 1.1 プロトコルを使用して情報を送受信します。

Tip

.NET 6 以降を対象とする WinUI 3 アプリでは、System.Net.Http.HttpClient (.NET HttpClient) も使用できます。 IHttpClientFactory、キャンセル トークン、最新の非同期パターンがサポートされています。 資格情報プロンプト、WinRT ブローカーによる Cookie 管理、Windows ネットワーク分離との統合など、WinRT 固有の機能が必要な場合は、Windows.Web.Http.HttpClientを使用します。 .NET WinUI 3 アプリでの単純な HTTP 要求の場合、多くの場合、System.Net.Http.HttpClientは簡単です。

HttpClient と Windows.Web.Http 名前空間の概要

Windows内のクラス。Web.Http 名前空間と関連するWindows。Web.Http.HeadersWindows。Web.Http.Filters 名前空間は、基本的な GET 要求を実行したり、以下に示すより高度な HTTP 機能を実装したりするための HTTP クライアントとして機能するWindows アプリのプログラミング インターフェイスを提供します。

  • 一般的な動詞 (DELETEGETPUTPOST) のメソッド。 これらの各要求は非同期操作として送信されます。

  • 一般的な認証設定とパターンのサポート。

  • トランスポートに関する Secure Sockets Layer (SSL) の詳細へのアクセス。

  • 高度なアプリにカスタマイズされたフィルターを含める機能。

  • Cookie を取得、設定、削除する機能。

  • 非同期メソッドで使用できる HTTP 要求の進行状況情報。

Windows.Web.Http.HttpRequestMessage クラスは、Windows.Web.Http.HttpClient によって送信される HTTP 要求メッセージを表します。 Windows。Web.Http.HttpResponseMessage クラスは、HTTP 要求から受信した HTTP 応答メッセージを表します。 HTTP メッセージは、IETF によって RFC 2616 で定義されています。

Windows。Web.Http 名前空間は、HTTP エンティティ本文として HTTP コンテンツを表し、Cookie を含むヘッダーを表します。 HTTP コンテンツは、HTTP 要求または HTTP 応答に関連付けることができます。 Windows。Web.Http 名前空間には、HTTP コンテンツを表すさまざまなクラスが用意されています。

  • HttpBufferContent。 バッファーとしてのコンテンツ
  • HttpFormUrlEncodedContentapplication/x-www-form-urlencoded MIME タイプでエンコードされた名前と値のタプルとしてのコンテンツ
  • HttpMultipartContentマルチパート/* MIME タイプの形式のコンテンツ。
  • HttpMultipartFormDataContentマルチパート/フォーム データ MIME タイプとしてエンコードされたコンテンツ。
  • HttpStreamContent。 ストリームとしてのコンテンツ (内部型は、データを受信するために HTTP GET メソッドによって使用され、データをアップロードする HTTP POST メソッドによって使用されます)
  • HttpStringContent。 文字列としてのコンテンツ。
  • IHttpContent - 開発者が独自のコンテンツ オブジェクトを作成するための基本インターフェイス

「HTTP 経由で単純な GET 要求を送信する」セクションのコード スニペットでは、 HttpStringContent クラスを使用して、HTTP GET 要求からの HTTP 応答を文字列として表します。

Windows。Web.Http.Headers 名前空間では、HTTP ヘッダーと Cookie の作成がサポートされ、HttpRequestMessage オブジェクトと HttpResponseMessage オブジェクトにプロパティとして関連付けられます。

HTTP 経由で単純な GET 要求を送信する

この記事で前述したように、Windows。Web.Http 名前空間を使用すると、Windows アプリで GET 要求を送信できます。 次のコード スニペットは、http://www.contoso.com クラスを使用して に GET 要求を送信し、Windows.Web.Http.HttpResponseMessage クラスを使用して GET 要求に対する応答を読み取る方法を示しています。

//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();

//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;

//The safe way to add a header value is to use the TryParseAdd method and verify the return value is true,
//especially if the header value is coming from user input.
string header = "MyApp/1.0";
if (!headers.UserAgent.TryParseAdd(header))
{
    throw new Exception("Invalid header value: " + header);
}

Uri requestUri = new Uri("https://www.contoso.com");

//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";

try
{
    //Send the GET request
    httpResponse = await httpClient.GetAsync(requestUri);
    httpResponse.EnsureSuccessStatusCode();
    httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
    httpResponseBody = "Error: " + ex.HResult.ToString("X") + " Message: " + ex.Message;
}
// pch.h
#pragma once
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Http.Headers.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"
#include <iostream>
using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    init_apartment();

    // Create an HttpClient object.
    Windows::Web::Http::HttpClient httpClient;

    // Add a user-agent header to the GET request.
    auto headers{ httpClient.DefaultRequestHeaders() };

    // The safe way to add a header value is to use the TryParseAdd method, and verify the return value is true.
    // This is especially important if the header value is coming from user input.
    std::wstring header{ L"MyApp/1.0" };
    if (!headers.UserAgent().TryParseAdd(header))
    {
        throw L"Invalid header value: " + header;
    }

    Uri requestUri{ L"https://www.contoso.com" };

    // Send the GET request asynchronously, and retrieve the response as a string.
    Windows::Web::Http::HttpResponseMessage httpResponseMessage;
    std::wstring httpResponseBody;

    try
    {
        // Send the GET request.
        httpResponseMessage = httpClient.GetAsync(requestUri).get();
        httpResponseMessage.EnsureSuccessStatusCode();
        httpResponseBody = httpResponseMessage.Content().ReadAsStringAsync().get();
    }
    catch (winrt::hresult_error const& ex)
    {
        httpResponseBody = ex.message();
    }
    std::wcout << httpResponseBody;
}

HTTP 経由でバイナリ データを POST する

の C++/WinRT コード例は、フォーム データと POST 要求を使用して、少量のバイナリ データをファイルのアップロードとして Web サーバーに送信する方法を示しています。 このコードでは 、HttpBufferContent クラスを使用してバイナリ データを表し、 HttpMultipartFormDataContent クラスを使用してマルチパート フォーム データを表します。

Note

(次のコード例に示すように) get を呼び出すことは、UI スレッドには適していません。 その場合に使用する正しい手法については、「 C++/WinRT を使用したコンカレンシーと非同期操作」を参照してください。

// pch.h
#pragma once
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Security.Cryptography.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Web.Http.Headers.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"
#include <iostream>
#include <sstream>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;

int main()
{
    init_apartment();

    auto buffer{
        Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary(
            L"A sentence of text to encode into binary to serve as sample data.",
            Windows::Security::Cryptography::BinaryStringEncoding::Utf8
        )
    };
    Windows::Web::Http::HttpBufferContent binaryContent{ buffer };
    // You can use the 'image/jpeg' content type to represent any binary data;
    // it's not necessarily an image file.
    binaryContent.Headers().Append(L"Content-Type", L"image/jpeg");

    Windows::Web::Http::Headers::HttpContentDispositionHeaderValue disposition{ L"form-data" };
    binaryContent.Headers().ContentDisposition(disposition);
    // The 'name' directive contains the name of the form field representing the data.
    disposition.Name(L"fileForUpload");
    // Here, the 'filename' directive is used to indicate to the server a file name
    // to use to save the uploaded data.
    disposition.FileName(L"file.dat");

    Windows::Web::Http::HttpMultipartFormDataContent postContent;
    postContent.Add(binaryContent); // Add the binary data content as a part of the form data content.

    // Send the POST request asynchronously, and retrieve the response as a string.
    Windows::Web::Http::HttpResponseMessage httpResponseMessage;
    std::wstring httpResponseBody;

    try
    {
        // Send the POST request.
        Uri requestUri{ L"https://www.contoso.com/post" };
        Windows::Web::Http::HttpClient httpClient;
        httpResponseMessage = httpClient.PostAsync(requestUri, postContent).get();
        httpResponseMessage.EnsureSuccessStatusCode();
        httpResponseBody = httpResponseMessage.Content().ReadAsStringAsync().get();
    }
    catch (winrt::hresult_error const& ex)
    {
        httpResponseBody = ex.message();
    }
    std::wcout << httpResponseBody;
}

(上記で使用した明示的なバイナリ データではなく) 実際のバイナリ ファイルの内容を POST するには、 HttpStreamContent オブジェクトを使用する方が簡単です。 1 つを構築し、そのコンストラクターへの引数として、 StorageFile.OpenReadAsync への呼び出しから返された値を渡します。 そのメソッドは、バイナリ ファイル内のデータのストリームを返します。

また、大きなファイル (約 10 MB を超える) をアップロードする場合は、Windows ランタイムバックグラウンド転送 API を使用することをお勧めします。

HTTP 経由で JSON データを POST する

次の例では、いくつかの JSON をエンドポイントにポストし、応答本文を書き出します。

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Web.Http;

private async Task TryPostJsonAsync()
{
    try
    {
        // Construct the HttpClient and Uri. This endpoint is for test purposes only.
        HttpClient httpClient = new HttpClient();
        Uri uri = new Uri("https://www.contoso.com/post");

        // Construct the JSON to post.
        HttpStringContent content = new HttpStringContent(
            "{ \"firstName\": \"Eliot\" }",
            UnicodeEncoding.Utf8,
            "application/json");

        // Post the JSON and wait for a response.
        HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
            uri,
            content);

        // Make sure the post succeeded, and write out the response.
        httpResponseMessage.EnsureSuccessStatusCode();
        var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
        Debug.WriteLine(httpResponseBody);
    }
    catch (Exception ex)
    {
        // Write out any exceptions.
        Debug.WriteLine(ex);
    }
}
// pch.h
#pragma once
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Security.Cryptography.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Web.Http.Headers.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"
#include <iostream>
#include <sstream>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;

int main()
{
    init_apartment();

    Windows::Web::Http::HttpResponseMessage httpResponseMessage;
    std::wstring httpResponseBody;

    try
    {
        // Construct the HttpClient and Uri. This endpoint is for test purposes only.
        Windows::Web::Http::HttpClient httpClient;
        Uri requestUri{ L"https://www.contoso.com/post" };

        // Construct the JSON to post.
        Windows::Web::Http::HttpStringContent jsonContent(
            L"{ \"firstName\": \"Eliot\" }",
            UnicodeEncoding::Utf8,
            L"application/json");

        // Post the JSON, and wait for a response.
        httpResponseMessage = httpClient.PostAsync(
            requestUri,
            jsonContent).get();

        // Make sure the post succeeded, and write out the response.
        httpResponseMessage.EnsureSuccessStatusCode();
        httpResponseBody = httpResponseMessage.Content().ReadAsStringAsync().get();
        std::wcout << httpResponseBody.c_str();
    }
    catch (winrt::hresult_error const& ex)
    {
        std::wcout << ex.message().c_str();
    }
}

エラーを処理する

HttpClient で行われた呼び出しは、2 つの異なる方法で失敗する可能性があり、それぞれを異なる方法で処理します。

  • サーバーがエラー状態コードで応答します。 要求は完了しますが、サーバーは 4xx または 5xx 状態コード (たとえば、404 Not Found または 503 Service Unavailable) を返します。 これは例外をスローしませんGetAsync または PostAsync 呼び出しは通常、false プロパティが である HttpResponseMessage を返します。
  • 応答を受信する前に要求が失敗します。 クライアントは交換をまったく完了できません (たとえば、ネットワーク接続がない、ホスト名が解決されない、接続がタイムアウトする、TLS ネゴシエーションが失敗するなど)。 これによって例外がスローされます。

返されたエラー状態コードはスローされないため、例外のみをチェックするだけでは不十分です。 応答を検査 し、例外を キャッチします。

応答状態コードを確認する

HttpResponseMessage.IsSuccessStatusCode を読み取り、サーバーが成功 (2xx) コードを返したかどうかをテストします。 特定のコードで分岐するには、 HttpResponseMessage.StatusCode ( HttpStatusCode 値) と ReasonPhrase を読み取ります。

前の例のように EnsureSuccessStatusCode を呼び出すことは、状態コードが成功コードでない場合に例外をスローするショートカットであるため、1 つの catch ブロックで両方のエラー モードを処理できます。 成功していないコードをエラーとして扱う場合にのみ呼び出します。 4xx または 5xx 応答の応答本文を読み取る場合は、代わりに IsSuccessStatusCode 確認してください。

Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
Uri requestUri = new Uri("https://www.contoso.com");

Windows.Web.Http.HttpResponseMessage response = await httpClient.GetAsync(requestUri);

if (response.IsSuccessStatusCode)
{
    string body = await response.Content.ReadAsStringAsync();
    // Process the successful response.
}
else
{
    // The server responded, but with an error status code.
    System.Diagnostics.Debug.WriteLine($"Request failed: {(int)response.StatusCode} {response.StatusCode} ({response.ReasonPhrase})");
}

ネットワーク例外を分類する

応答を受信する前に要求がスローされた場合 (たとえば、名前を解決できない場合や、接続が失敗したりタイムアウトしたりした場合)、例外の HResult は基になるネットワーク エラーを識別します。 それを Windows.Web.WebError.GetStatus に渡して、原因を示す WebErrorStatus 値(たとえば、HostNameNotResolvedCannotConnectTimeout、または ConnectionReset)を取得します。 これを使用して、ユーザーに通知するか、フォールバックするか、再試行するかを決定します。

WebError.GetStatus は、サーバーが応答したため、HTTP エラー応答 (4xx または 5xx 状態コード) には適用されません。 HttpResponseMessage.IsSuccessStatusCode または HttpResponseMessage.StatusCode を調べて、EnsureSuccessStatusCodeを呼び出す代わりにそれらを処理します。

Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
Uri requestUri = new Uri("https://www.contoso.com");

try
{
    Windows.Web.Http.HttpResponseMessage response = await httpClient.GetAsync(requestUri);
    if (!response.IsSuccessStatusCode)
    {
        // Handle the non-success HTTP status code (for example, 404 Not Found or 503 Service Unavailable).
        System.Diagnostics.Debug.WriteLine($"HTTP error: {(int)response.StatusCode} {response.StatusCode}");
        return;
    }
    string body = await response.Content.ReadAsStringAsync();
    // Process the successful response.
}
catch (Exception ex)
{
    Windows.Web.WebErrorStatus status = Windows.Web.WebError.GetStatus(ex.HResult);

    switch (status)
    {
        case Windows.Web.WebErrorStatus.HostNameNotResolved:
        case Windows.Web.WebErrorStatus.CannotConnect:
        case Windows.Web.WebErrorStatus.Timeout:
        case Windows.Web.WebErrorStatus.ConnectionReset:
            // A transient connectivity problem. Retrying with backoff may succeed.
            System.Diagnostics.Debug.WriteLine($"Network error: {status}.");
            break;
        case Windows.Web.WebErrorStatus.Unknown:
            // GetStatus couldn't map the HRESULT to a WebErrorStatus value.
            System.Diagnostics.Debug.WriteLine($"Unmapped error. HRESULT: 0x{ex.HResult:X8} {ex.Message}");
            break;
        default:
            System.Diagnostics.Debug.WriteLine($"Web error: {status}");
            break;
    }
}

C++/WinRT でも、GetStatusへの入力として winrt::hresult_error::code を使用して同じパターンが適用されます。

// #include <winrt/Windows.Web.h>
try
{
    auto response{ httpClient.GetAsync(requestUri).get() };
    if (!response.IsSuccessStatusCode())
    {
        // Handle the non-success HTTP status code (for example, 404 Not Found or 503 Service Unavailable).
    }
    else
    {
        auto body{ response.Content().ReadAsStringAsync().get() };
        // Process the successful response.
    }
}
catch (winrt::hresult_error const& ex)
{
    Windows::Web::WebErrorStatus status{ Windows::Web::WebError::GetStatus(ex.code()) };

    if (status == Windows::Web::WebErrorStatus::HostNameNotResolved ||
        status == Windows::Web::WebErrorStatus::CannotConnect ||
        status == Windows::Web::WebErrorStatus::Timeout ||
        status == Windows::Web::WebErrorStatus::ConnectionReset)
    {
        // A transient connectivity problem. Retrying with backoff may succeed.
    }
    else
    {
        // Inspect status, or fall back to ex.code() and ex.message().
    }
}

一時的なエラーを再試行する

HttpClient は、失敗したリクエストを自動的に再試行しません。 一時的なエラー (上に示した接続エラー、または 429 要求が多すぎる、503 サービス利用不可、504 ゲートウェイ タイムアウトなど) の場合は、指数バックオフを使用して要求を再試行し、サーバーが送信するときに Retry-After 応答ヘッダーを受け入れる必要があります。 試行回数を制限し、400 Bad Request や 404 Not Found などの一時的でないエラーを再試行しないでください。

Windows.Web.Http の例外

Uniform Resource Identifier (URI) の無効な文字列が Windows.Foundation.Uri オブジェクトのコンストラクターに渡されると、例外がスローされます。

.NET:Windows。Foundation.Uri 型は、C# および VB で System.Uri として表示されます。

C# および Visual Basic では、このエラーは、.NET 4.5 の System.Uri クラスと System.Uri.TryCreate メソッドの 1 つを使用して、URI が構築される前にユーザーから受信した文字列をテストすることで回避できます。

C++ では、文字列を URI に解析しようとするメソッドはありません。 アプリが Windows.Foundation.Uri を生成するための入力をユーザーから受け取る場合、コンストラクターの呼び出しは try/catch ブロックで囲む必要があります。 例外が発生した場合、アプリはユーザーに通知し、新しいホスト名の入力を求めることができます。

Windows。Web.Http には便利な関数がありません。 そのため、 HttpClient とこの名前空間の他のクラスを使用するアプリでは、 HRESULT 値を使用する必要があります。

C++/WinRT を使用するアプリでは、winrt::hresult_error 構造体はアプリの実行中に発生する例外を表します。 winrt::hresult_error::code 関数は、特定の例外に割り当てられた HRESULT を返します。 winrt::hresult_error::message 関数は、HRESULT 値に関連付けられているシステム指定の文字列を返します。 詳細については、「C++/WinRT でのエラー処理」を参照してください。

使用可能な HRESULT 値は、 Winerror.h ヘッダー ファイルに一覧表示されます。 アプリでは、特定の HRESULT 値をフィルター処理して、例外の原因に応じてアプリの動作を変更できます。

C#、VB.NET で .NET Framework 4.5 を使用するアプリでは、System.Exception は、例外が発生したときにアプリの実行中にエラーを表します。 System.Exception.HResult プロパティは、特定の例外に割り当てられた HRESULT を返します。 System.Exception.Message プロパティは、例外を説明するメッセージを返します。

C++/CX は C++/WinRT に置き換わりました。 ただし、C++/CX を使用するアプリでは、 Platform::Exception は例外 が発生したときにアプリの実行中にエラーを表します。 Platform::Exception::HResult プロパティは、特定の例外に割り当てられた HRESULT を返します。 Platform::Exception::Message プロパティは、HRESULT 値に関連付けられているシステム指定の文字列を返します。

ほとんどのパラメーター検証エラーでは、返される HRESULTE_INVALIDARG。 一部の無効なメソッド呼び出しの場合、返される HRESULTE_ILLEGAL_METHOD_CALL