Zelfstudie: HTTP-aanvragen maken in een .NET-console-app met behulp van C #

In deze zelfstudie wordt een app gebouwd die HTTP-aanvragen verzendt naar een REST-service op GitHub. De app leest informatie in JSON-indeling en converteert de JSON naar C#-objecten. Het converteren van JSON-naar C#-objecten wordt deserialisatie genoemd.

In de zelfstudie ziet u het volgende:

  • HTTP-aanvragen verzenden.
  • JSON-antwoorden deserialiseren.
  • Configureer deserialisatie met kenmerken.

Als u liever het laatste voorbeeld voor deze zelfstudie volgt, kunt u het downloaden. Zie Voorbeelden en zelfstudies voor downloadinstructies.

Vereisten

  • .NET SDK 6.0 of hoger
  • Een code-editor zoals [Visual Studio Code (een open-source, platformoverschrijdende editor). U kunt de voorbeeld-app uitvoeren in Windows, Linux of macOS of in een Docker-container.

De client-app maken

  1. Open een opdrachtprompt en maak een nieuwe map voor uw app. Maak hiervan de huidige map.

  2. Typ de volgende opdracht in het consolevenster:

    dotnet new console --name WebAPIClient
    

    Met deze opdracht maakt u de startersbestanden voor een eenvoudige Hallo wereld-app. De projectnaam is 'WebAPIClient'.

  3. Navigeer naar de map WebAPIClient en voer de app uit.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run wordt automatisch uitgevoerd dotnet restore om eventuele afhankelijkheden te herstellen die de app nodig heeft. Het wordt ook uitgevoerd dotnet build als dat nodig is. Als het goed is, ziet u de uitvoer van "Hello, World!"de app. Druk in de terminal op Ctrl+C om de app te stoppen.

HTTP-aanvragen doen

Deze app roept de GitHub-API aan om informatie op te halen over de projecten onder de paraplu van .NET Foundation . Het eindpunt is https://api.github.com/orgs/dotnet/repos. Om informatie op te halen, wordt er een HTTP GET-aanvraag ingediend. Browsers maken ook HTTP GET-aanvragen, zodat u die URL in de adresbalk van uw browser kunt plakken om te zien welke informatie u ontvangt en verwerkt.

Gebruik de HttpClient klasse om HTTP-aanvragen te doen. HttpClient ondersteunt alleen asynchrone methoden voor de langlopende API's. Met de volgende stappen maakt u dus een asynchrone methode en roept u deze aan vanuit de methode Main.

  1. Open het bestand in de Program.cs projectmap en vervang de inhoud door het volgende:

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

    Deze code:

    • Hiermee wordt de Console.WriteLine -instructie vervangen door een aanroep van ProcessRepositoriesAsync die het await trefwoord gebruikt.
    • Hiermee definieert u een lege ProcessRepositoriesAsync methode.
  2. Gebruik in de Program klasse een HttpClient om aanvragen en antwoorden af te handelen door de inhoud te vervangen door de volgende C#.

    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)
    {
    }
    

    Deze code:

    • Http-headers instellen voor alle aanvragen:
      • Een Accept header om JSON-antwoorden te accepteren
      • Een User-Agent koptekst. Deze headers worden gecontroleerd door de GitHub-servercode en zijn nodig om informatie op te halen uit GitHub.
  3. Roep in de ProcessRepositoriesAsync methode het GitHub-eindpunt aan dat een lijst met alle opslagplaatsen onder de .NET Foundation-organisatie retourneert:

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

    Deze code:

    • Wacht op de taak die is geretourneerd door de aanroepmethode HttpClient.GetStringAsync(String) . Met deze methode wordt een HTTP GET-aanvraag naar de opgegeven URI verzonden. De hoofdtekst van het antwoord wordt geretourneerd als een String, die beschikbaar is wanneer de taak is voltooid.
    • De antwoordreeks json wordt naar de console afgedrukt.
  4. Bouw de app en voer deze uit.

    dotnet run
    

    Er is geen buildwaarschuwing omdat de ProcessRepositoriesAsync nu een await operator bevat. De uitvoer is een lange weergave van JSON-tekst.

Het JSON-resultaat deserialiseren

Met de volgende stappen wordt het JSON-antwoord geconverteerd naar C#-objecten. U gebruikt de System.Text.Json.JsonSerializer klasse om JSON te deserialiseren in objecten.

  1. Maak een bestand met de naam Repository.cs en voeg de volgende code toe:

    public record class Repository(string name);
    

    De voorgaande code definieert een klasse die het JSON-object vertegenwoordigt dat wordt geretourneerd door de GitHub-API. U gebruikt deze klasse om een lijst met namen van opslagplaatsen weer te geven.

    De JSON voor een opslagplaatsobject bevat tientallen eigenschappen, maar alleen de name eigenschap wordt gedeserialiseerd. De serializer negeert automatisch JSON-eigenschappen waarvoor geen overeenkomst is in de doelklasse. Met deze functie kunt u eenvoudiger typen maken die werken met slechts een subset velden in een groot JSON-pakket.

    De C#-conventie is om de eerste letter van eigenschapsnamen een hoofdletter te geven, maar de name eigenschap hier begint met een kleine letter, omdat deze precies overeenkomt met wat er in de JSON staat. Later ziet u hoe u C#-eigenschapsnamen gebruikt die niet overeenkomen met de namen van de JSON-eigenschappen.

  2. Gebruik de serializer om JSON te converteren naar C#-objecten. Vervang de aanroep in GetStringAsync(String) de ProcessRepositoriesAsync methode door de volgende regels:

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

    De bijgewerkte code vervangt GetStringAsync(String) door GetStreamAsync(String). Deze serialisatiemethode gebruikt een stroom in plaats van een tekenreeks als bron.

    Het eerste argument voor JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) is een await expressie. await expressies kunnen bijna overal in uw code worden weergegeven, ook al hebt u ze tot nu toe alleen gezien als onderdeel van een toewijzingsinstructie. De andere twee parameters, JsonSerializerOptions en CancellationToken, zijn optioneel en worden weggelaten in het codefragment.

    De DeserializeAsync methode is algemeen, wat betekent dat u typeargumenten opgeeft voor het soort objecten dat moet worden gemaakt van de JSON-tekst. In dit voorbeeld deserialiseert u naar een List<Repository>. Dit is een ander algemeen object, een System.Collections.Generic.List<T>. De List<T> klasse slaat een verzameling objecten op. Het argument type declareert het type objecten dat is opgeslagen in de List<T>. Het argument type is uw Repository record, omdat de JSON-tekst een verzameling opslagplaatsobjecten vertegenwoordigt.

  3. Voeg code toe om de naam van elke opslagplaats weer te geven. Vervang de regels met de volgende tekst:

    Console.Write(json);
    

    met de volgende code:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.Write(repo.name);
    
  4. De volgende using instructies moeten boven aan het bestand staan:

    using System.Net.Http.Headers;
    using System.Text.Json;
    
  5. Voer de app uit.

    dotnet run
    

    De uitvoer is een lijst met de namen van de opslagplaatsen die deel uitmaken van de .NET Foundation.

Deserialisatie configureren

  1. Vervang in Repository.cs de bestandsinhoud door de volgende C#.

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

    Deze code:

    • Wijzigt de naam van de name eigenschap in Name.
    • Voegt de JsonPropertyNameAttribute toe om op te geven hoe deze eigenschap wordt weergegeven in de JSON.
  2. Werk in Program.cs de code bij om het nieuwe hoofdlettergebruik van de Name eigenschap te gebruiken:

    foreach (var repo in repositories)
       Console.Write(repo.Name);
    
  3. Voer de app uit.

    De uitvoer is hetzelfde.

De code herstructureren

De ProcessRepositoriesAsync methode kan het asynchrone werk doen en een verzameling van de opslagplaatsen retourneren. Wijzig die methode om te retourneren Task<List<Repository>>en verplaats de code die naar de console schrijft in de buurt van de aanroeper.

  1. Wijzig de handtekening van ProcessRepositoriesAsync om een taak te retourneren waarvan het resultaat een lijst Repository met objecten is:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Retourneer de opslagplaatsen na het verwerken van het JSON-antwoord:

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

    De compiler genereert het Task<T> -object voor de retourwaarde omdat u deze methode hebt gemarkeerd als async.

  3. Wijzig het bestand Program.cs en vervang de aanroep naar ProcessRepositoriesAsync door het volgende om de resultaten vast te leggen en de naam van elke opslagplaats naar de console te schrijven.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.Write(repo.Name);
    
  4. Voer de app uit.

    De uitvoer is hetzelfde.

Meer eigenschappen deserialiseren

Met de volgende stappen voegt u code toe om meer eigenschappen in het ontvangen JSON-pakket te verwerken. U wilt waarschijnlijk niet elke eigenschap verwerken, maar als u er nog een paar toevoegt, ziet u andere functies van C#.

  1. Vervang de inhoud van Repository klasse door de volgende record definitie:

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

    De Uri typen en int hebben ingebouwde functionaliteit om te converteren naar en van tekenreeksweergave. Er is geen extra code nodig om te deserialiseren van de JSON-tekenreeksindeling naar deze doeltypen. Als het JSON-pakket gegevens bevat die niet worden geconverteerd naar een doeltype, genereert de serialisatieactie een uitzondering.

  2. Werk de foreach lus in het bestand Program.cs bij om de eigenschapswaarden weer te geven:

    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. Voer de app uit.

    De lijst bevat nu de aanvullende eigenschappen.

Een datumeigenschap toevoegen

De datum van de laatste pushbewerking wordt op deze manier opgemaakt in het JSON-antwoord:

2016-02-08T21:27:00Z

Deze indeling is voor Coordinated Universal Time (UTC), dus het resultaat van deserialisatie is een DateTime waarde waarvan Kind de eigenschap is Utc.

Als u een datum en tijd wilt weergeven in uw tijdzone, moet u een aangepaste conversiemethode schrijven.

  1. Voeg in Repository.cs een eigenschap toe voor de UTC-weergave van de datum en tijd en een eigenschap readonly LastPush die de datum retourneert die is geconverteerd naar de lokale tijd. Het bestand moet er als volgt uitzien:

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

    De LastPush eigenschap wordt gedefinieerd met behulp van een expressie-bodied lid voor de get accessor. Er is geen set toegangsbeheer. Het weglaten van de set accessor is een manier om een eigenschap met het kenmerk Alleen-lezen in C# te definiƫren. (Ja, u kunt alleen-schrijven-eigenschappen maken in C#, maar de waarde ervan is beperkt.)

  2. Voeg nog een uitvoerinstructie toe in Program.cs: opnieuw:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. De volledige app moet lijken op het volgende Program.cs-bestand :

    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. Voer de app uit.

    De uitvoer bevat de datum en tijd van de laatste push naar elke opslagplaats.

Volgende stappen

In deze zelfstudie hebt u een app gemaakt die webaanvragen doet en de resultaten parseert. Uw versie van de app moet nu overeenkomen met het voltooide voorbeeld.

Meer informatie over het configureren van JSON-serialisatie vindt u in JSON serialiseren en deserialiseren (marshal en unmarshal) in .NET.