HttpClient

Wichtige APIs

Verwenden Sie HttpClient und den rest der Windows. Web.Http-Namespace-API zum Senden und Empfangen von Informationen mithilfe der HTTP 2.0- und HTTP 1.1-Protokolle.

Tipp

WinUI 3-Apps, die auf .NET 6 oder höher abzielen, können auch System.Net.Http.HttpClient (den .NET-HttpClient) verwenden. Es unterstützt IHttpClientFactory, Abbruchtoken und moderne asynchrone Muster. Verwenden Sie diese FunktionWindows.Web.Http.HttpClient, wenn Sie WinRT-spezifische Features wie Anmeldeinformationsaufforderungen, Cookieverwaltung über den WinRT-Broker oder die Integration mit Windows Netzwerkisolation benötigen. Für einfache HTTP-Anfragen in einer .NET WinUI 3-App ist System.Net.Http.HttpClient oft einfacher.

Übersicht über HttpClient und die Windows. Web.Http-Namespace

Die Klassen im Windows. Web.Http-Namespace und die zugehörigen Windows. Web.Http.Headers und Windows. Web.Http.Filters-Namespaces stellen eine Programmierschnittstelle für Windows Apps bereit, die als HTTP-Client fungieren, um grundlegende GET-Anforderungen auszuführen oder erweiterte HTTP-Funktionen zu implementieren, die unten aufgeführt sind.

  • Methoden für allgemeine Verben (DELETE, GET, PUT und POST). Jede dieser Anfragen wird asynchron gesendet.

  • Unterstützung für allgemeine Authentifizierungseinstellungen und -muster.

  • Zugriff auf SSL-Details (Secure Sockets Layer) für den Transport.

  • Möglichkeit zum Einschließen von benutzerdefinierten Filtern in erweiterte Apps.

  • Möglichkeit zum Abrufen, Festlegen und Löschen von Cookies.

  • Informationen zum Fortschritt von HTTP-Anforderungen, die für asynchrone Methoden verfügbar sind.

Die Windows.Web.Http.HttpRequestMessage-Klasse stellt eine HTTP-Anforderungsnachricht dar, die von Windows.Web.Http.HttpClient gesendet wird. Die Windows. Die Web.Http.HttpResponseMessage-Klasse stellt eine HTTP-Antwortnachricht dar, die von einer HTTP-Anforderung empfangen wurde. HTTP-Nachrichten werden in RFC 2616 vom IETF definiert.

Der Windows.Web.Http-Namespace repräsentiert HTTP-Inhalte als HTTP-Entitätskörper und Header einschließlich Cookies. HTTP-Inhalte können einer HTTP-Anforderung oder einer HTTP-Antwort zugeordnet werden. Der Windows.Web.Http-Namespace bietet eine Reihe verschiedener Klassen zur Darstellung von HTTP-Inhalten.

Der Codeausschnitt im Abschnitt "Senden einer einfachen GET-Anforderung über HTTP" verwendet die HttpStringContent-Klasse , um die HTTP-Antwort einer HTTP GET-Anforderung als Zeichenfolge darzustellen.

Der Namespace Windows.Web.Http.Headers unterstützt das Erstellen von HTTP-Headern und -Cookies, die dann HttpRequestMessage- und HttpResponseMessage-Objekten als Eigenschaften zugeordnet werden.

Senden einer einfachen GET-Anforderung über HTTP

Wie bereits weiter oben in diesem Artikel erwähnt, ermöglicht der Windows.Web.Http-Namespace es Windows-Apps, GET-Anforderungen zu senden. Der folgende Codeausschnitt veranschaulicht, wie mithilfe der Klasse http://www.contoso.com eine GET-Anforderung an gesendet und mithilfe der Klasse Windows.Web.Http.HttpResponseMessage die Antwort auf die GET-Anforderung gelesen wird.

//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;
}

POST-Binärdaten über HTTP

Im folgenden C++/WinRT-Codebeispiel wird die Verwendung von Formulardaten und einer POST-Anforderung veranschaulicht, um eine kleine Menge binärer Daten als Dateiupload an einen Webserver zu senden. Der Code verwendet die HttpBufferContent-Klasse , um die Binärdaten darzustellen, und die HttpMultipartFormDataContent-Klasse , um die mehrteiligen Formulardaten darzustellen.

Hinweis

Das Aufrufen von Get (wie im folgenden Codebeispiel dargestellt) ist für einen UI-Thread nicht geeignet. Die richtige Technik, die in diesem Fall verwendet werden soll, finden Sie unter "Parallelität" und "asynchrone Vorgänge mit 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;
}

Um den Inhalt einer tatsächlichen Binärdatei (anstelle der oben verwendeten expliziten Binärdaten) zu veröffentlichen, ist es einfacher, ein HttpStreamContent-Objekt zu verwenden. Erstellen Sie eins, und übergeben Sie als Argument an den Konstruktor den von einem Aufruf an StorageFile.OpenReadAsync zurückgegebenen Wert. Diese Methode gibt einen Stream für die in Ihrer Binärdatei enthaltenen Daten zurück.

Wenn Sie eine große Datei hochladen (größer als ca. 10 MB), sollten Sie auch die Windows-Runtime Hintergrundübertragungs-APIs verwenden.

POST JSON-Daten über HTTP

Im folgenden Beispiel wird ein JSON-Code an einen Endpunkt gesendet und anschließend der Antworttext geschrieben.

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();
    }
}

Fehler behandeln

Ein Aufruf mit HttpClient kann auf zwei unterschiedliche Arten fehlschlagen, und jede davon behandeln Sie anders.

  • Der Server antwortet mit einem Fehlerstatuscode. Die Anforderung wird abgeschlossen, aber der Server gibt einen 4xx- oder 5xx-Statuscode zurück (z. B. 404 Nicht gefunden oder 503 Dienst nicht verfügbar). Dies löst keine Ausnahme aus. Der Aufruf GetAsync oder PostAsync gibt normalerweise eine HttpResponseMessage zurück, deren IsSuccessStatusCode-Eigenschaft auf false festgelegt ist.
  • Die Anforderung schlägt fehl, bevor eine Antwort empfangen wird. Der Client kann den Datenaustausch überhaupt nicht abschließen (z. B. gibt es keine Netzwerkverbindung, der Hostname wird nicht aufgelöst, bei der Verbindung kommt es zu einer Zeitüberschreitung oder die TLS-Aushandlung schlägt fehl). Dadurch wird eine Ausnahme ausgelöst.

Da ein zurückgegebener Fehlerstatuscode nicht ausgelöst wird, reicht die Überprüfung nur auf Ausnahmen aus. Überprüfen Sie die Antwort und erfassen Sie Ausnahmen.

Überprüfen des Antwortstatuscodes

Lesen Sie HttpResponseMessage.IsSuccessStatusCode , um zu testen, ob der Server einen Erfolgscode (2xx) zurückgegeben hat. Lesen Sie "HttpResponseMessage.StatusCode " (einen HttpStatusCode-Wert ) und "ReasonPhrase", um den spezifischen Code zu verzweigen.

Das Aufrufen von EnsureSuccessStatusCode ist wie in den vorherigen Beispielen eine Verknüpfung, die eine Ausnahme auslöst, wenn der Statuscode kein Erfolgscode ist, sodass Sie beide Fehlermodi in einem einzigen catch Block behandeln können. Verwenden Sie dies nur, wenn ein Code, der keinen Erfolg signalisiert, als Fehler gewertet werden soll. Wenn Sie den Antworttext einer 4xx- oder 5xx-Antwort lesen möchten, überprüfen Sie IsSuccessStatusCode stattdessen.

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})");
}

Klassifizieren einer Netzwerk exception

Wenn bei einer Anforderung eine Ausnahme ausgelöst wird, bevor eine Antwort empfangen wurde (z. B. wenn der Name nicht aufgelöst werden kann oder die Verbindung fehlschlägt oder eine Zeitüberschreitung auftritt), gibt HResult der Ausnahme den zugrunde liegenden Netzwerkfehler an. Übergeben Sie es an Windows.Web.WebError.GetStatus, um einen WebErrorStatus-Wert abzurufen, der die Ursache beschreibt (z. B. HostNameNotResolved, CannotConnect, Timeout oder ConnectionReset). Verwenden Sie sie, um zu entscheiden, ob der Benutzer benachrichtigt, auf eine Ausweichlösung zurückgegriffen oder ein erneuter Versuch unternommen werden soll.

WebError.GetStatus gilt nicht für HTTP-Fehlerantworten (4xx- oder 5xx-Statuscodes), da der Server reagiert hat. Überprüfen Sie HttpResponseMessage.IsSuccessStatusCode oder HttpResponseMessage.StatusCode, um diese Fälle zu behandeln, anstatt EnsureSuccessStatusCode aufzurufen.

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;
    }
}

Das gleiche Muster gilt in C++/WinRT, wobei winrt::hresult_error::code als Eingabe für GetStatus verwendet wird.

// #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().
    }
}

Wiederholen bei vorübergehenden Fehlern

HttpClient wiederholt keine fehlgeschlagenen Anforderungen für Sie. Bei vorübergehenden Fehlern (den oben gezeigten Verbindungsfehlern oder Serverstatuscodes wie 429 Zu viele Anfragen, 503 Dienst nicht verfügbar und 504 Gateway-Timeout) senden Sie die Anfrage selbst mit exponentiellem Backoff erneut und beachten Sie den Antwortheader Retry-After, wenn der Server einen sendet. Begrenzen Sie die Anzahl der Versuche, und wiederholen Sie keine nicht vorübergehenden Fehler wie 400 Bad Request oder 404 Not Found.

Ausnahmen in Windows.Web.Http

Eine Ausnahme wird ausgelöst, wenn eine ungültige Zeichenfolge für einen Uniform Resource Identifier (URI) an den Konstruktor für das Windows.Foundation.Uri-Objekt übergeben wird.

.NET: Der Windows.Foundation.Uri-Typ wird in C# und VB als System.Uri angezeigt.

In C# und Visual Basic kann dieser Fehler vermieden werden, indem die System.Uri-Klasse in der .NET 4.5 und eine der System.Uri.TryCreate-Methoden verwendet werden, um die von einem Benutzer empfangene Zeichenfolge zu testen, bevor der URI erstellt wird.

In C++ gibt es keine Methode, eine Zeichenfolge als URI zu parsen. Wenn eine App Benutzereingaben für den Konstruktor von Windows.Foundation.Uri erhält, sollte sich der Konstruktor in einem Try/Catch-Block befinden. Wenn eine Ausnahme ausgelöst wird, kann die App den Benutzer benachrichtigen und einen neuen Hostnamen anfordern.

Die Windows. Web.Http verfügt nicht über eine Komfortfunktion. Daher muss eine App, die HttpClient und andere Klassen in diesem Namespace verwendet, den HRESULT-Wert verwenden.

In Apps mit C++/WinRT stellt die Struktur winrt::hresult_error eine Ausnahme dar, die während der App-Ausführung ausgelöst wird. Die winrt::hresult_error::code-Funktion gibt das HRESULT zurück, das der spezifischen Ausnahme zugewiesen ist. Die winrt::hresult_error::message-Funktion gibt die vom System bereitgestellte Zeichenfolge zurück, die dem HRESULT-Wert zugeordnet ist. Weitere Informationen finden Sie unter Fehlerbehandlung mit C++/WinRT

Mögliche HRESULT-Werte werden in der Winerror.h-Headerdatei aufgeführt. Ihre App kann nach bestimmten HRESULT-Werten filtern, um das App-Verhalten je nach Ausnahmeursache zu ändern.

In Apps, die das .NET Framework 4.5 in C#, VB.NET verwenden, stellt die System.Exception einen Fehler während der App-Ausführung dar, wenn eine Ausnahme auftritt. Die System.Exception.HResult-Eigenschaft gibt das HRESULT zurück, das der spezifischen Ausnahme zugewiesen ist. Die System.Exception.Message-Eigenschaft gibt die Nachricht zurück, die die Ausnahme beschreibt.

C++/CX wurde von C++/WinRT abgelöst. In Apps mit C++/CX stellt die Platform::Exception jedoch einen Fehler während der App-Ausführung dar, wenn eine Ausnahme auftritt. Die Platform::Exception::HResult-Eigenschaft gibt das HRESULT zurück, das der spezifischen Ausnahme zugewiesen ist. Die Platform::Exception::Message-Eigenschaft gibt die vom System bereitgestellte Zeichenfolge zurück, die dem HRESULT-Wert zugeordnet ist.

Bei den meisten Fehlern bei der Parametervalidierung ist das zurückgegebene HRESULTE_INVALIDARG. Bei einigen unzulässigen Methodenaufrufen ist das zurückgegebene HRESULTE_ILLEGAL_METHOD_CALL.