Condividi tramite


Esercitazione: Effettuare richieste HTTP in un'app console .NET con C#

Questa esercitazione crea un'app che invia richieste HTTP a un servizio REST in GitHub. L'app legge le informazioni in formato JSON e converte il codice JSON in oggetti C#. La conversione da json a oggetti C# è nota come deserializzazione .

L'esercitazione illustra come:

  • Inviare richieste HTTP.
  • Deserializzare le risposte JSON.
  • Configurare la deserializzazione con gli attributi.

Se preferisci seguire l'esercitazione finale di questo tutorial, puoi scaricarla. Per istruzioni sul download, vedere esempi ed esercitazioni .

Prerequisiti

  • La versione più recente .NET SDK
  • editor di Visual Studio Code
  • Il DevKit C#

Creare l'app client

  1. Aprire un prompt dei comandi e creare una nuova directory per la tua app. Imposta la directory corrente.

  2. Immettere il comando seguente in una finestra della console:

    dotnet new console --name WebAPIClient
    

    Questo comando crea i file di avvio per un'app "Hello World" di base. Il nome del progetto è "WebAPIClient".

  3. Passare alla directory "WebAPIClient" ed eseguire l'app.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run esegue automaticamente dotnet restore per ripristinare le dipendenze necessarie all'app. Viene inoltre eseguito dotnet build, se necessario. Dovresti vedere l'output dell'app "Hello, World!". Nel terminale premere CTRL+C per arrestare l'app.

Effettuare richieste HTTP

Questa app chiama l'API GitHub per ottenere informazioni sui progetti nell'ambito .NET Foundation. L'endpoint è https://api.github.com/orgs/dotnet/repos. Per recuperare informazioni, effettua una richiesta HTTP GET. I browser effettuano anche richieste HTTP GET, in modo da poter incollare l'URL nella barra degli indirizzi del browser per visualizzare le informazioni che verranno ricevute ed elaborate.

Usare la classe HttpClient per effettuare richieste HTTP. HttpClient supporta solo metodi asincroni per le API a esecuzione prolungata. I passaggi seguenti creano quindi un metodo asincrono e lo chiamano dal metodo Main.

  1. Aprire il file Program.cs nella directory del progetto e sostituirlo con quanto segue:

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

    Questo codice:

    • Sostituisce l'istruzione Console.WriteLine con una chiamata a ProcessRepositoriesAsync che usa la parola chiave await.
    • Definisce un metodo ProcessRepositoriesAsync vuoto.
  2. Nella classe Program usare un HttpClient per gestire richieste e risposte sostituendo il contenuto con il codice C#seguente.

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

    Questo codice:

    • Configura le intestazioni HTTP per tutte le richieste:
      • Intestazione Accept per accettare risposte JSON
      • Intestazione User-Agent. Queste intestazioni vengono controllate dal codice del server GitHub e sono necessarie per recuperare informazioni da GitHub.
  3. Nel metodo ProcessRepositoriesAsync chiamare l'endpoint GitHub che restituisce un elenco di tutti i repository nell'organizzazione .NET Foundation:

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

    Questo codice:

    • Attende l'attività restituita dalla chiamata al metodo HttpClient.GetStringAsync(String). Questo metodo invia una richiesta HTTP GET all'URI specificato. Il corpo della risposta viene restituito come String, disponibile al termine dell'attività.
    • La stringa di risposta json viene stampata nella console.
  4. Compilare l'app ed eseguirla.

    dotnet run
    

    Non viene visualizzato alcun avviso di compilazione perché il ProcessRepositoriesAsync contiene ora un operatore await. L'output è una visualizzazione lunga del testo JSON.

Deserializzare il risultato JSON

I passaggi seguenti semplificano l'approccio per recuperare i dati ed elaborarli. Si userà il GetFromJsonAsync metodo di estensione che fa parte del 📦 pacchetto NuGet System.Net.Http.Json per recuperare e deserializzare i risultati JSON in oggetti.

  1. Creare un file denominato Repository.cs e aggiungere il codice seguente:

    public record class Repository(string Name);
    

    Il codice precedente definisce una classe per rappresentare l'oggetto JSON restituito dall'API GitHub. Questa classe verrà usata per visualizzare un elenco di nomi di repository.

    Il codice JSON per un oggetto repository contiene decine di proprietà, ma solo la proprietà Name verrà deserializzata. Il serializzatore ignora automaticamente le proprietà JSON per le quali non esiste alcuna corrispondenza nella classe di destinazione. Questa funzionalità semplifica la creazione di tipi che funzionano solo con un subset di campi in un pacchetto JSON di grandi dimensioni.

    Anche se il GetFromJsonAsync metodo che verrà usato nel punto successivo ha un vantaggio di non distinzione tra maiuscole e minuscole quando si tratta di nomi di proprietà, la convenzione C# consiste nel maiuscolare la prima lettera di nomi di proprietà.

  2. Usare il HttpClientJsonExtensions.GetFromJsonAsync metodo per recuperare e convertire JSON in oggetti C#. Sostituire la chiamata a GetStringAsync(String) nel metodo ProcessRepositoriesAsync con le righe seguenti:

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

    Il codice aggiornato sostituisce GetStringAsync(String) con HttpClientJsonExtensions.GetFromJsonAsync.

    Il primo argomento da usare per il GetFromJsonAsync metodo è un'espressione await . Esistono espressioni await che possono comparire quasi ovunque nel codice, anche se fino ad ora le hai viste solo come parte di un'istruzione di assegnamento. Il parametro successivo è requestUri facoltativo e non deve essere specificato se è già stato specificato durante la creazione dell'oggetto client . Non è stato specificato l'oggetto con l'URI client a cui inviare la richiesta, quindi è stato specificato l'URI. L'ultimo parametro facoltativo, viene CancellationToken omesso nel frammento di codice.

    Il GetFromJsonAsync metodo è generico, il che significa che si forniscono argomenti di tipo per il tipo di oggetti da creare dal testo JSON recuperato. In questo esempio, stai deserializzando a un List<Repository>, che è un altro oggetto generico, un System.Collections.Generic.List<T>. La classe List<T> archivia una raccolta di oggetti. L'argomento type dichiara il tipo di oggetti archiviati nella List<T>. L'argomento type è il record Repository, perché il testo JSON rappresenta una raccolta di oggetti del repository.

  3. Aggiungere il codice per visualizzare il nome di ogni repository. Sostituire le righe che leggono:

    Console.Write(json);
    

    con il seguente codice:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.WriteLine(repo.Name);
    
  4. Nella parte superiore del file devono essere presenti le direttive using seguenti:

    using System.Net.Http.Headers;
    using System.Net.Http.Json;
    
  5. Esegui l'app.

    dotnet run
    

    L'output è un elenco dei nomi dei repository che fanno parte di .NET Foundation.

Eseguire il refactoring del codice

Il metodo ProcessRepositoriesAsync può eseguire il lavoro asincrono e restituire una raccolta dei repository. Modificare tale metodo per restituire Task<List<Repository>>e spostare il codice che scrive nella console vicino al chiamante.

  1. Modificare la firma di ProcessRepositoriesAsync per restituire un'attività il cui risultato è un elenco di oggetti Repository:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Restituire i repository dopo l'elaborazione della risposta JSON:

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

    Il compilatore genera l'oggetto Task<T> per il valore restituito perché questo metodo è stato contrassegnato come async.

  3. Modificare il file Program.cs, sostituendo la chiamata a ProcessRepositoriesAsync con quanto segue per acquisire i risultati e scrivere ogni nome del repository nella console.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
    
  4. Esegui l'app.

    L'output è lo stesso.

Deserializzare altre proprietà

Nei passaggi seguenti il codice viene esteso per elaborare altre proprietà dal payload JSON restituito dall'API GitHub. Probabilmente non sarà necessario elaborare tutte le proprietà, ma l'aggiunta di alcuni dimostra funzionalità C# aggiuntive.

  1. Sostituire il contenuto della Repository classe con la definizione seguente record . Assicurarsi di importare lo System.Text.Json.Serialization spazio dei nomi e applicare l'attributo [JsonPropertyName] per mappare esplicitamente i campi JSON alle proprietà C#.

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

    I tipi Uri e int dispongono di funzionalità predefinite per la conversione da e verso la rappresentazione di stringa. Non è necessario alcun codice aggiuntivo per deserializzare dal formato stringa JSON a tali tipi di destinazione. Se il pacchetto JSON contiene dati che non vengono convertiti in un tipo di destinazione, l'azione di serializzazione genera un'eccezione.

    JSON spesso usa lowercase o snake_case per i nomi delle proprietà. I campi come html_url e pushed_at non seguono le convenzioni di denominazione C# PascalCase. L'uso [JsonPropertyName] garantisce che queste chiavi JSON siano associate correttamente alle proprietà C# corrispondenti, anche quando i nomi differiscono nel caso o contengono caratteri di sottolineatura. Questo approccio garantisce la deserializzazione prevedibile e stabile, consentendo al tempo stesso i nomi delle proprietà PascalCase in C#. Inoltre, il GetFromJsonAsync metodo è case-insensitive quando i nomi delle proprietà corrispondono, quindi non è necessaria alcuna ulteriore conversione.

  2. Aggiornare il ciclo foreach nel file Program.cs per visualizzare i valori delle proprietà:

    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. Esegui l'app.

    L'elenco include ora le proprietà aggiuntive.

Aggiungere una proprietà date

La data dell'ultima operazione push viene formattata in questo modo nella risposta JSON:

2016-02-08T21:27:00Z

Questo formato è relativo all'ora UTC (Coordinated Universal Time), pertanto il risultato della deserializzazione è un valore DateTime la cui proprietà Kind è Utc.

Per ottenere una data e un'ora rappresentate nel fuso orario, è necessario scrivere un metodo di conversione personalizzato.

  1. In Repository.csaggiungere una proprietà per la rappresentazione UTC della data e dell'ora e una proprietà LastPush di sola lettura che restituisce la data convertita nell'ora locale, il file dovrebbe essere simile al seguente:

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

    La proprietà LastPush viene definita utilizzando un membro con corpo di espressione per la funzione di accesso get. Non esiste alcun accessore set. Omettere l'accessor set è un modo per definire una proprietà di sola lettura in C#. Sì, è possibile creare proprietà di sola scrittura in C#, ma il loro valore è limitato.

  2. Aggiungere un'altra istruzione di output in Program.cs: di nuovo:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. L'app completa dovrebbe essere simile al file di Program.cs seguente:

    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. Esegui l'app.

    L'output include la data e l'ora dell'ultimo push in ogni repository.

Passaggi successivi

In questa esercitazione è stata creata un'app che effettua richieste Web e analizza i risultati. La versione dell'app dovrebbe ora corrispondere al esempio completato.

Scopri di più su come configurare la serializzazione JSON in Come serializzare e deserializzare JSON in .NET.