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 konverterar JSON-svaret till C#-objekt. Du använder klassen System.Text.Json.JsonSerializer för att deserialisera JSON 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.

    C#-konventionen är att versalisera första bokstaven i egenskapsnamn, men här börjar egenskapen name med en gemen bokstav eftersom det exakt matchar det som finns i JSON. Senare får du se hur du använder C#-egenskapsnamn som inte matchar JSON-egenskapsnamnen.

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

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

    Den uppdaterade koden ersätter GetStringAsync(String) med GetStreamAsync(String). Den här serialiserarmetoden använder en ström i stället för en sträng som källa.

    Det första argumentet för att JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) ä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. De andra två parametrarna, JsonSerializerOptions och CancellationToken, är valfria och utelämnas i kodfragmentet.

    Metoden DeserializeAsync är allmän, vilket innebär att du anger typargument för vilken typ av objekt som ska skapas från 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.Write(repo.name);
    
  4. Följande using direktiv bör finnas överst i filen:

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

    dotnet run
    

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

Konfigurera deserialisering

  1. I Repository.csersätter du filinnehållet med följande C#.

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

    Den här koden:

    • Ändrar namnet på egenskapen name till Name.
    • Lägger till JsonPropertyNameAttribute för att ange hur den här egenskapen ska visas i JSON.
  2. I Program.csuppdaterar du koden för att använda den nya versaliseringen av egenskapen Name.

    foreach (var repo in repositories)
       Console.Write(repo.Name);
    
  3. Kör appen.

    Resultatet är detsamma.

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:

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

    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.Write(repo.Name);
    
  4. Kör appen.

    Resultatet är detsamma.

Deserialisera fler egenskaper

Följande steg lägger till kod för att bearbeta fler av egenskaperna i det mottagna JSON-paketet. Du vill förmodligen inte bearbeta alla egenskaper, men om du lägger till några fler visas andra funktioner i C#.

  1. Ersätt innehållet i Repository-klassen med följande 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);
    

    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.

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

    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.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. 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.