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

Den här självstudien skapar en app som utfärdar HTTP-begäranden till en REST-tjänst på GitHub. Appen läser information i JSON-format och konverterar JSON till C#-objekt. Konvertering från JSON till C#-objekt kallas deserialisering.

Självstudien visar hur du:

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

Om du föredrar att följa med i det slutliga exemplet för den här självstudien kan du ladda ned det. Instruktioner för nedladdning finns i Exempel och självstudier.

Krav

  • .NET SDK 6.0 eller senare
  • En kodredigerare som [Visual Studio Code (en plattformsoberoende redigerare med öppen källkod). Du kan köra exempelappen i Windows, Linux eller macOS eller i en Docker-container.

Skapa klientappen

  1. Öppna en kommandotolk och skapa en ny katalog för din app. Gör det 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örs dotnet restore automatiskt för att återställa eventuella beroenden som appen behöver. Den körs dotnet build också om det behövs. Du bör se appens utdata "Hello, World!". Tryck på Ctrl+C i terminalen för att stoppa appen.

Göra HTTP-begäranden

Den här appen anropar GitHub-API :et för att få information om projekten under .NET Foundation-paraplyet . 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å du kan klistra in webbadressen i webbläsarens adressfält för att se vilken information du kommer att ta emot och bearbeta.

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

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

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

    Den här koden:

    • Ersätter -instruktionen Console.WriteLine med ett anrop till ProcessRepositoriesAsync som använder nyckelordet await .
    • Definierar en tom ProcessRepositoriesAsync metod.
  2. Program I klassen 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 sidhuvud 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. ProcessRepositoriesAsync I metoden 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å uppgiften som returneras från anropande HttpClient.GetStringAsync(String) metod. Den här metoden skickar en HTTP GET-begäran till den angivna URI:n. Brödtexten i svaret returneras som en String, som är tillgänglig när uppgiften slutförs.
    • Svarssträngen json skrivs ut till konsolen.
  4. Skapa appen och kör den.

    dotnet run
    

    Det finns ingen byggvarning eftersom nu ProcessRepositoriesAsync 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 System.Text.Json.JsonSerializer klassen 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);
    

    Koden ovan 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 name egenskapen 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 av fälten i ett stort JSON-paket.

    C#-konventionen är att kapitalisera den första bokstaven i egenskapsnamn, men egenskapen här börjar med en gemen name bokstav eftersom den matchar exakt vad 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 ProcessRepositoriesAsync metoden 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 till JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) är ett await uttryck. await -uttryck kan visas nästan var som helst i koden, även om du hittills bara har sett dem som en del av en tilldelningsinstruktor. 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 avserialiserar du till ett , som är ett List<Repository>annat allmänt objekt, ett 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.cs ersä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å name egenskapen till Name.
    • JsonPropertyNameAttribute Lägger till för att ange hur den här egenskapen ska visas i JSON.
  2. I Program.cs uppdaterar du koden så att den använder egenskapens Name nya versaler:

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

    Utdata är desamma.

Omstrukturera koden

Metoden ProcessRepositoriesAsync kan utföra asynkront arbete och returnera en samling lagringsplatser. Ändra metoden för att returnera Task<List<Repository>>och flytta koden som skriver till konsolen nära anroparen.

  1. Ändra signaturen ProcessRepositoriesAsync för att returnera en aktivitet vars resultat är en lista med 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 filen Program.cs och ersätt anropet till ProcessRepositoriesAsync med följande för att samla in resultatet och skriva varje lagringsplatsnamn till konsolen.

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

    Utdata är desamma.

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 loopen foreach 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 som representeras i tidszonen måste du skriva en anpassad konverteringsmetod.

  1. I Repository.cs lägger du till en egenskap för UTC-representationen av datum och tid och en skrivskyddad LastPush egenskap som returnerar det datum som konverterats till lokal tid. 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 expression-bodied medlem för get accessorn. Det finns ingen set accessor. Att set utelämna 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 har du skapat en app som gör webbbegäranden och parsar resultatet. Din version av appen bör nu matcha det färdiga exemplet.

Läs mer om hur du konfigurerar JSON-serialisering i Serialisera och deserialisera (marskalk och omarshal) JSON i .NET.