Tutorial: Übermitteln von HTTP-Anforderungen in einer .NET-Konsolen-App mit C#

In diesem Tutorial erstellen Sie eine App, die HTTP-Anforderungen an einen REST-Dienst in GitHub übermittelt. Die App liest Informationen im JSON-Format und konvertiert den JSON-Code in C#-Objekte. Die Konvertierung von JSON-Code in C#-Objekte wird als Deserialisierung bezeichnet.

In diesem Tutorial lernen Sie Folgendes:

  • Senden von HTTP-Anforderungen
  • Deserialisieren von JSON-Antworten
  • Konfigurieren der Deserialisierung mit Attributen

Wenn Sie lieber das vollständige Beispiel für dieses Tutorial befolgen möchten, können Sie es herunterladen. Anweisungen zum Herunterladen finden Sie unter Beispiele und Lernprogramme.

Voraussetzungen

  • .NET SDK 6.0 oder höher
  • Ein Code-Editor wie [Visual Studio Code, (ein plattformübergreifender Open-Source-Editor). Sie können die Beispiel-App unter Windows, Linux oder macOS oder auch in einem Docker-Container ausführen.

Erstellen der Client-App

  1. Öffnen Sie eine Eingabeaufforderung, und erstellen Sie ein neues Verzeichnis für Ihre App. Legen Sie das Verzeichnis als aktuelles Verzeichnis fest.

  2. Geben Sie den folgenden Befehl in ein Konsolenfenster ein:

    dotnet new console --name WebAPIClient
    

    Mit diesem Befehl werden die Startdateien für eine einfache „Hallo Welt“-App erstellt. Der Projektname lautet „WebAPIClient“.

  3. Navigieren Sie zum Verzeichnis „WebAPIClient“, und führen Sie die App aus.

    cd WebAPIClient
    
    dotnet run
    

    Mit dotnet run wird automatisch dotnet restore ausgeführt, um alle Abhängigkeiten wiederherzustellen, die von der App benötigt werden. Außerdem wird bei Bedarf dotnet build ausgeführt. Sie sollten die Ausgabe "Hello, World!" der App sehen. Drücken Sie in Ihrem Terminal STRG+C, um die App zu beenden.

Übermitteln von HTTP-Anforderungen

Diese App ruft die GitHub-API auf, um Informationen zu den Projekten unter dem Schirm der .NET Foundation zu erhalten. Der Endpunkt ist https://api.github.com/orgs/dotnet/repos. Zum Abrufen von Informationen wird eine HTTP GET-Anforderung gesendet. Browser verwenden ebenfalls HTTP GET-Anforderungen, daher können Sie diese URL auf der Adressleiste Ihres Browsers einfügen, um zu sehen, welche Informationen abgerufen und verarbeitet werden.

Verwenden Sie die HttpClient-Klasse, um HTTP-Anforderungen zu übermitteln. HttpClient unterstützt für die APIs mit langer Ausführungszeit nur asynchrone Methoden. Daher wird mit den folgenden Schritten eine asynchrone Methode erstellt und über die Main-Methode aufgerufen.

  1. Öffnen Sie die Datei Program.cs in Ihrem Projektverzeichnis und ersetzen Sie ihren Inhalt durch Folgendes:

    await ProcessRepositoriesAsync();
    
    static async Task ProcessRepositoriesAsync(HttpClient client)
    {
    }
    

    Mit diesem Code wird Folgendes durchgeführt:

    • Ersetzen der Console.WriteLine-Anweisung durch einen Aufruf von ProcessRepositoriesAsync, der das Schlüsselwort await verwendet
    • Definiert eine leere ProcessRepositoriesAsync-Methode.
  2. Verwenden Sie in der Program-Klasse einen HttpClient, um Anforderungen und Antworten zu verarbeiten, indem Sie den Inhalt durch das folgende C# ersetzen.

    using System.Net.Http.Headers;
    
    using HttpClient client = new();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");
    
    await ProcessRepositoriesAsync(client);
    
    static async Task ProcessRepositoriesAsync(HttpClient client)
    {
    }
    

    Mit diesem Code wird Folgendes durchgeführt:

    • Einrichten der HTTP-Header für alle Anforderungen:
      • Ein Accept-Header zum Akzeptieren von JSON-Antworten
      • Ein User-Agent-Header. Diese Header werden vom GitHub-Servercode überprüft und sind erforderlich, um Informationen aus GitHub abzurufen.
  3. Rufen Sie in der ProcessRepositoriesAsync-Methode den GitHub-Endpunkt auf, der eine Liste aller Repositorys unter der Organisation der .NET Foundation zurückgibt:

     static async Task ProcessRepositoriesAsync(HttpClient client)
     {
         var json = await client.GetStringAsync(
             "https://api.github.com/orgs/dotnet/repos");
    
         Console.Write(json);
     }
    

    Mit diesem Code wird Folgendes durchgeführt:

    • Wartet auf den Task, der vom Aufrufen der HttpClient.GetStringAsync(String)-Methode zurückgegeben wird. Diese Methode sendet eine HTTP GET-Anforderung an den angegebenen URI. Der Text der Antwort wird als String zurückgegeben, der nach Abschluss der Aufgabe verfügbar ist.
    • Die Antwortzeichenfolge json wird in der Konsole ausgegeben.
  4. Erstellen Sie die App, und führen Sie sie aus.

    dotnet run
    

    Es wird keine Buildwarnung ausgegeben, da ProcessRepositoriesAsync jetzt einen await-Operator enthält. Die Ausgabe ist eine lange Darstellung von JSON-Text.

Deserialisieren des JSON-Ergebnisses

Mit den folgenden Schritten wird die JSON-Antwort in C#-Objekte konvertiert. Sie verwenden die System.Text.Json.JsonSerializer-Klasse, um JSON-Code in Objekte zu deserialisieren.

  1. Erstellen Sie eine Datei mit dem Namen Repository.cs, und fügen Sie den folgenden Code hinzu:

    public record class Repository(string name);
    

    Mit diesem Code definieren Sie eine Klasse, die das von der GitHub-API zurückgegebene JSON-Objekt darstellt. Sie verwenden diese Klasse, um eine Liste mit Repositorynamen anzuzeigen.

    Der JSON-Code für ein Repositoryobjekt enthält Dutzende von Eigenschaften, aber nur die name-Eigenschaft wird deserialisiert. Das Serialisierungsmodul ignoriert automatisch alle JSON-Eigenschaften, für die keine Übereinstimmung in der Zielklasse vorliegt. Dieses Feature vereinfacht die Erstellung von Typen, die nur mit einem Teilsatz der Felder in einem großen JSON-Paket funktionieren.

    Laut C#-Konvention wird der erste Buchstabe von Eigenschaftennamen groß geschrieben, aber die name-Eigenschaft beginnt hier mit einem Kleinbuchstaben, da dies genau dem JSON-Code entspricht. Später erfahren Sie, wie Sie C#-Eigenschaftennamen verwenden, die nicht mit den JSON-Eigenschaftennamen übereinstimmen.

  2. Verwenden Sie das Serialisierungsmodul, um JSON-Code in C#-Objekte zu konvertieren. Ersetzen Sie den Aufruf von GetStringAsync(String) in der ProcessRepositoriesAsync-Methode durch die folgenden Zeilen:

    await using Stream stream =
        await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
    var repositories =
        await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
    

    Im aktualisierten Code wird GetStringAsync(String) durch GetStreamAsync(String) ersetzt. Die Methode des Serialisierungsmoduls nutzt anstelle einer Zeichenfolge einen Stream als Quelle.

    Das erste Argument für JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) ist ein await-Ausdruck. await-Ausdrücke können fast überall in Ihrem Code stehen, obwohl sie bisher nur als Teil einer Zuweisungsanweisung verwendet wurden. Die anderen beiden Parameter JsonSerializerOptions und CancellationToken sind optional und werden im Codeausschnitt ausgelassen.

    Die DeserializeAsync-Methode ist generisch. Das bedeutet, dass Sie Typargumente für die Objekttypen angeben müssen, die aus dem JSON-Text erstellt werden sollen. In diesem Beispiel deserialisieren Sie in ein List<Repository>. Dabei handelt es sich um ein weiteres generisches Objekt, System.Collections.Generic.List<T>. Die List<T>-Klasse speichert eine Sammlung von Objekten. Das Typargument deklariert den Typ der in List<T> gespeicherten Objekte. Das Typargument ist Ihr Repository-Datensatz, da der JSON-Text eine Auflistung von Repositoryobjekten darstellt.

  3. Fügen Sie Code hinzu, um die Namen der einzelnen Repositorys anzuzeigen. Ersetzen Sie diese Zeilen:

    Console.Write(json);
    

    durch den folgenden Code:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.Write(repo.name);
    
  4. Die folgenden using-Anweisungen sollten am Anfang der Datei vorhanden sein:

    using System.Net.Http.Headers;
    using System.Text.Json;
    
  5. Führen Sie die App aus.

    dotnet run
    

    Die Ausgabe ist eine Liste der Namen der Repositorys, die zur .NET Foundation gehören.

Konfigurieren der Deserialisierung

  1. Ersetzen Sie in Repository.cs den Inhalt der Datei durch die folgende C#.

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name);
    

    Mit diesem Code wird Folgendes durchgeführt:

    • Ändert den Namen der name-Eigenschaft in Name.
    • Fügt das JsonPropertyNameAttribute hinzu, um anzugeben, wie diese Eigenschaft im JSON-Code angezeigt wird.
  2. Aktualisieren Sie in Program.cs den Code so, dass er die neue Groß-/Kleinschreibung der Name-Eigenschaft verwendet:

    foreach (var repo in repositories)
       Console.Write(repo.Name);
    
  3. Führen Sie die App aus.

    Die Ausgabe ist identisch.

Gestalten Sie den Code um.

Die ProcessRepositoriesAsync-Methode kann die asynchrone Arbeit erledigen und eine Auflistung der Repositorys zurückgeben. Ändern Sie diese Methode, um Task<List<Repository>> zurückzugeben, und verschieben Sie den Code, der in die Konsole schreibt, in die Nähe des Aufrufers.

  1. Ändern Sie die Signatur von ProcessRepositoriesAsync, um einen Task zurückzugeben, dessen Ergebnis eine Liste mit Repository-Objekten ist:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Geben Sie die Repositorys zurück, nachdem die JSON-Antwort verarbeitet wurde:

    await using Stream stream =
        await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
    var repositories =
        await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
    return repositories ?? new();
    

    Der Compiler generiert das Task<T>-Objekt für den Rückgabewert, da Sie diese Methode als async markiert haben.

  3. Ändern Sie die Datei Program.cs, und ersetzen Sie den Aufruf von ProcessRepositoriesAsync durch Folgendes, um die Ergebnisse zu erfassen und die einzelnen Repositorynamen in die Konsole zu schreiben.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.Write(repo.Name);
    
  4. Führen Sie die App aus.

    Die Ausgabe ist identisch.

Deserialisieren weiterer Eigenschaften

In den folgenden Schritten fügen Sie Code hinzu, um weitere Eigenschaften im empfangenen JSON-Paket zu verarbeiten. Wahrscheinlich möchten Sie nicht jede Eigenschaft verarbeiten, aber das Hinzufügen einiger weiterer veranschaulicht andere Features von C#.

  1. Ersetzen Sie den Inhalt der Klasse Repository durch die folgende record-Definition:

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name,
        [property: JsonPropertyName("description")] string Description,
        [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl,
        [property: JsonPropertyName("homepage")] Uri Homepage,
        [property: JsonPropertyName("watchers")] int Watchers);
    

    Die Typen Uri und int verfügen über integrierte Funktionen zum Konvertieren in und aus Zeichenfolgendarstellungen. Es ist kein zusätzlicher Code erforderlich, um das JSON-Zeichenfolgenformat in diese Zieltypen zu deserialisieren. Wenn das JSON-Paket Daten enthält, die nicht in einen Zieltyp konvertiert werden können, löst die Serialisierungsaktion eine Ausnahme aus.

  2. Aktualisieren Sie die foreach-Schleife in der Datei Program.cs, um die Eigenschaftswerte anzuzeigen:

    foreach (var repo in repositories)
    {
        Console.WriteLine($"Name: {repo.Name}");
        Console.WriteLine($"Homepage: {repo.Homepage}");
        Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}");
        Console.WriteLine($"Description: {repo.Description}");
        Console.WriteLine($"Watchers: {repo.Watchers:#,0}");
        Console.WriteLine();
    }
    
  3. Führen Sie die App aus.

    Die Liste enthält jetzt die zusätzlichen Eigenschaften.

Hinzufügen einer Datumseigenschaft

Das Datum des letzten Pushvorgangs wird in der JSON-Antwort wie folgt formatiert:

2016-02-08T21:27:00Z

Dieses Format entspricht der koordinierten Weltzeit (UTC), sodass das Ergebnis der Deserialisierung ein DateTime-Wert ist, dessen Kind-Eigenschaft Utc lautet.

Wenn Sie ein Datum in Ihrer Zeitzone bevorzugen, müssen Sie eine benutzerdefinierte Methode für die Konvertierung schreiben.

  1. Fügen Sie in Repository.cs eine Eigenschaft für die UTC-Darstellung des Datums und der Uhrzeit sowie eine LastPush Eigenschaft hinzu, die das in die Ortszeit umgerechnete Datum zurückgibt. Die Datei sollte wie folgt aussehen:

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name,
        [property: JsonPropertyName("description")] string Description,
        [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl,
        [property: JsonPropertyName("homepage")] Uri Homepage,
        [property: JsonPropertyName("watchers")] int Watchers,
        [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc)
    {
        public DateTime LastPush => LastPushUtc.ToLocalTime();
    }
    

    Die LastPush-Eigenschaft wird mit einem Ausdruckskörpermember für die get-Zugriffsmethode definiert. Es gibt keine set-Zugriffsmethode. Das Auslassen der set-Zugriffsmethode ist eine Möglichkeit, eine schreibgeschützte Eigenschaft in C# zu definieren. (Ja, Sie können lesegeschützte Eigenschaften in C# erstellen, aber ihr Wert ist begrenzt.)

  2. Fügen Sie in Program.cs eine weitere Ausgabeanweisung hinzu:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. Die vollständige App sollte der folgenden Program.cs-Datei ähneln:

    using System.Net.Http.Headers;
    using System.Text.Json;
    
    using HttpClient client = new();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");
    
    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
    {
        Console.WriteLine($"Name: {repo.Name}");
        Console.WriteLine($"Homepage: {repo.Homepage}");
        Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}");
        Console.WriteLine($"Description: {repo.Description}");
        Console.WriteLine($"Watchers: {repo.Watchers:#,0}");
        Console.WriteLine($"{repo.LastPush}");
        Console.WriteLine();
    }
    
    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    {
        await using Stream stream =
            await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
        var repositories =
            await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
        return repositories ?? new();
    }
    
  4. Führen Sie die App aus.

    Die Ausgabe enthält das Datum und die Uhrzeit des jeweils letzten Pushs an jedes Repository.

Nächste Schritte

In diesem Tutorial haben Sie eine App erstellt, die Webanforderungen ausführt und die Ergebnisse analysiert. Ihre Version der App sollte nun dem vollständigen Beispiel entsprechen.

Weitere Informationen zum Konfigurieren der JSON-Serialisierung finden Sie unter Serialisieren und Deserialisieren (Marshallen und Rückgängigmachen des Marshallens) von JSON-Daten in .NET.