Dela via


Självstudie: Gör HTTP-begäranden i en .NET-konsolapp med C#

Den här handledningen bygger en app som skickar HTTP-begäranden till en REST-tjänst hos GitHub. Appen läser information i JSON-format och konverterar JSON till C#-objekt. Konvertering från JSON till C#-objekt kallas deserialisering.

Handledningen visar hur du:

  • Skicka HTTP-begäranden.
  • Deserialisera JSON-svar.
  • Konfigurera deserialisering med attribut.

Om du föredrar att följa med i den sista för den här självstudien kan du ladda ned den. Instruktioner för nedladdning finns i Exempelfiler och Handledningar.

Förutsättningar

Skapa klientappen

  1. Öppna en kommandotolk och skapa en ny katalog för din app. Gör den till den aktuella katalogen.

  2. Ange följande kommando i ett konsolfönster:

    dotnet new console --name WebAPIClient
    

    Det här kommandot skapar startfilerna för en grundläggande "Hello World"-app. Projektnamnet är "WebAPIClient".

  3. Navigera till katalogen "WebAPIClient" och kör appen.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run kör automatiskt dotnet restore för att återställa eventuella beroenden som appen behöver. Den kör också dotnet build om det behövs. Du bör se appens resultat "Hello, World!". I terminalen trycker du på Ctrl+C för att stoppa appen.

Att göra HTTP-förfrågningar

Den här appen anropar GitHub API- för att få information om projekten under .NET Foundation paraply. Slutpunkten är https://api.github.com/orgs/dotnet/repos. För att hämta information gör den en HTTP GET-begäran. Webbläsare gör också HTTP GET-begäranden så att du kan klistra in webbadressen i webbläsarens adressfält för att se vilken information du kommer att ta emot och bearbeta.

Använd klassen HttpClient för att göra HTTP-begäranden. HttpClient stöder endast asynkrona metoder för dess långvariga API:er. Följande steg skapar därför en asynkron metod och anropar den från main-metoden.

  1. Öppna filen Program.cs i projektkatalogen och ersätt dess innehåll med följande:

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

    Den här koden:

    • Ersätter Console.WriteLine-instruktionen med ett anrop till ProcessRepositoriesAsync som använder nyckelordet await.
    • Definierar en tom ProcessRepositoriesAsync-metod.
  2. I klassen Program använder du en HttpClient för att hantera begäranden och svar genom att ersätta innehållet med följande 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)
    {
    }
    

    Den här koden:

    • Konfigurerar HTTP-huvuden för alla begäranden:
      • Ett Accept-huvud för att acceptera JSON-svar
      • En User-Agent rubrik. Dessa huvuden kontrolleras av GitHub-serverkoden och är nödvändiga för att hämta information från GitHub.
  3. I metoden ProcessRepositoriesAsync anropar du GitHub-slutpunkten som returnerar en lista över alla lagringsplatser under .NET Foundation-organisationen:

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

    Den här koden:

    • Väntar på att uppgiften som returneras från att anropa HttpClient.GetStringAsync(String)-metoden. Den här metoden skickar en HTTP GET-begäran till den angivna URI:n. Svarets brödtext returneras som en String, som är tillgänglig när aktiviteten slutförs.
    • Svarssträngen json skrivs ut till konsolen.
  4. Skapa appen och kör den.

    dotnet run
    

    Det finns ingen kompileringsvarning eftersom ProcessRepositoriesAsync nu innehåller en await operator. Utdata är en lång visning av JSON-text.

Deserialisera JSON-resultatet

Följande steg förenklar metoden för att hämta data och bearbeta dem. Du använder GetFromJsonAsync tilläggsmetoden som ingår i 📦 NuGet-paketet System.Net.Http.Json för att hämta och deserialisera JSON-resultaten till objekt.

  1. Skapa en fil med namnet Repository.cs och lägg till följande kod:

    public record class Repository(string Name);
    

    Föregående kod definierar en klass som representerar JSON-objektet som returneras från GitHub-API:et. Du använder den här klassen för att visa en lista över lagringsplatsnamn.

    JSON för ett lagringsplatsobjekt innehåller dussintals egenskaper, men endast egenskapen Name kommer att deserialiseras. Serialiseraren ignorerar automatiskt JSON-egenskaper för vilka det inte finns någon matchning i målklassen. Den här funktionen gör det enklare att skapa typer som endast fungerar med en delmängd fält i ett stort JSON-paket.

    Även om den GetFromJsonAsync metod som du använder i nästa punkt har en fördel av att vara skiftlägeskänslig när det gäller egenskapsnamn, är C#-konventionen att kapitalisera den första bokstaven med egenskapsnamn.

  2. HttpClientJsonExtensions.GetFromJsonAsync Använd metoden för att hämta och konvertera JSON till C#-objekt. Ersätt anropet till GetStringAsync(String) i metoden ProcessRepositoriesAsync med följande rader:

    var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");
    

    Den uppdaterade koden ersätter GetStringAsync(String) med HttpClientJsonExtensions.GetFromJsonAsync.

    Det första argumentet för GetFromJsonAsync metoden är ett await uttryck. await uttryck kan förekomma nästan var som helst i din kod, även om du hittills bara har sett dem som en del av en tilldelningssats. Nästa parameter requestUri är valfri och behöver inte anges om den redan angavs när objektet skapades client . Du har inte angett objektet client med den URI som begäran ska skickas till, så du har angett URI:n nu. Den sista valfria parametern utelämnas CancellationToken i kodfragmentet.

    Metoden GetFromJsonAsync är allmän, vilket innebär att du anger typargument för vilken typ av objekt som ska skapas från den hämtade JSON-texten. I det här exemplet deserialiserar du till en List<Repository>, som är ett annat allmänt objekt, en System.Collections.Generic.List<T>. Klassen List<T> lagrar en samling objekt. Typargumentet deklarerar typen av objekt som lagras i List<T>. Typargumentet är din Repository post eftersom JSON-texten representerar en samling lagringsplatsobjekt.

  3. Lägg till kod för att visa namnet på varje lagringsplats. Ersätt raderna som lyder:

    Console.Write(json);
    

    med följande kod:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.WriteLine(repo.Name);
    
  4. Följande using direktiv bör finnas överst i filen:

    using System.Net.Http.Headers;
    using System.Net.Http.Json;
    
  5. Kör appen.

    dotnet run
    

    Utdata är en lista över namnen på de lagringsplatser som ingår i .NET Foundation.

Omstrukturera koden

Metoden ProcessRepositoriesAsync kan utföra asynkront arbete och returnera en samling lagringsplatser. Ändra den metoden så att den returnerar Task<List<Repository>>, och flytta koden som skriver till konsolen nära dess anropare.

  1. Ändra signaturen för ProcessRepositoriesAsync för att returnera en uppgift vars resultat är en lista över Repository objekt:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Returnera lagringsplatserna efter bearbetning av JSON-svaret:

    var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");
    return repositories ?? new();
    

    Kompilatorn genererar Task<T>-objektet för returvärdet eftersom du har markerat den här metoden som async.

  3. Ändra Program.cs-filen och ersätt anropet till ProcessRepositoriesAsync med följande för att samla in resultaten och skriva varje lagringsplatsnamn till konsolen.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
    
  4. Kör appen.

    Resultatet är detsamma.

Deserialisera fler egenskaper

I följande steg utökar vi koden för att bearbeta fler egenskaper från JSON-nyttolasten som returneras av GitHub-API:et. Du behöver förmodligen inte bearbeta alla egenskaper, men om du lägger till några exempel visas ytterligare C#-funktioner.

  1. Ersätt innehållet i Repository klassen med följande record definition. Importera System.Text.Json.Serialization namnområdet och använd [JsonPropertyName] attributet för att mappa JSON-fält till C#-egenskaper explicit.

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

    Typerna Uri och int har inbyggda funktioner för att konvertera till och från strängrepresentation. Ingen extra kod behövs för att deserialisera från JSON-strängformat till dessa måltyper. Om JSON-paketet innehåller data som inte konverteras till en måltyp utlöser serialiseringsåtgärden ett undantag.

    JSON använder lowercase ofta eller snake_case för egenskapsnamn. Fält som html_url och pushed_at följer inte namngivningskonventionerna för C# PascalCase. Genom att använda [JsonPropertyName] ser du till att dessa JSON-nycklar är korrekt bundna till motsvarande C#-egenskaper, även om deras namn skiljer sig åt om eller innehåller understreck. Den här metoden garanterar förutsägbar och stabil deserialisering samtidigt som egenskapsnamn för PascalCase tillåts i C#. Dessutom är GetFromJsonAsync-metoden case-insensitive vid matchning av egenskapsnamn, så ingen ytterligare konvertering krävs.

  2. Uppdatera foreach-loopen i filen Program.cs för att visa egenskapsvärdena:

    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. Kör appen.

    Listan innehåller nu de ytterligare egenskaperna.

Lägg till en datumegenskap

Datumet för den senaste push-åtgärden formateras på det här sättet i JSON-svaret:

2016-02-08T21:27:00Z

Det här formatet är för Coordinated Universal Time (UTC), så resultatet av deserialisering är ett DateTime värde vars Kind egenskap är Utc.

För att få ett datum och en tid representerad i tidszonen måste du skriva en anpassad konverteringsmetod.

  1. I Repository.cslägger du till en egenskap för UTC-representationen av datum och tid och en skrivskyddad LastPush egenskap som returnerar datumet konverterat till lokal tid, och filen bör se ut så här:

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

    Egenskapen LastPush definieras med hjälp av en uttrycksbaserad medlem för get-accessorn. Det finns ingen set accessor. Att utelämna set-accessorn är ett sätt att definiera en skrivskyddad egenskap i C#. (Ja, du kan skapa skrivskyddade egenskaper i C#, men deras värde är begränsat.)

  2. Lägg till ytterligare en utdatasats i Program.cs: igen:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. Den fullständiga appen bör likna följande Program.cs fil:

    using System.Net.Http.Headers;
    using System.Net.Http.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)
    {
        var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");
        return repositories ?? new List<Repository>();
    }
    
  4. Kör appen.

    Utdata innehåller datum och tid för den senaste push-överföringen till varje lagringsplats.

Nästa steg

I den här självstudien skapade du en app som gör webbbegäranden och parsar resultatet. Din version av appen ska nu matcha det färdiga exemplet.

Läs mer om hur du konfigurerar JSON-serialisering i Så här serialiserar och deserialiserar du (seriell och deseriell) JSON i .NET.