Udostępnij za pośrednictwem


Samouczek: tworzenie żądań HTTP w aplikacji konsolowej platformy .NET przy użyciu języka C#

W tym samouczku utworzysz aplikację, która wysyła żądania HTTP do usługi REST w usłudze GitHub. Aplikacja odczytuje informacje w formacie JSON i konwertuje kod JSON na obiekty języka C#. Konwertowanie z formatu JSON na obiekty języka C# jest nazywane deserializacją.

W samouczku pokazano, jak:

  • Wysyłanie żądań HTTP.
  • Deserializowanie odpowiedzi JSON.
  • Konfigurowanie deserializacji za pomocą atrybutów.

Jeśli wolisz wykonać czynności opisane w ostatnim przykładzie na potrzeby tego samouczka, możesz go pobrać. Aby uzyskać instrukcje dotyczące pobierania, zobacz Przykłady i samouczki.

Wymagania wstępne

  • Najnowszy .NET SDK
  • Edytor programu Visual Studio Code
  • Zestaw deweloperski C#

Tworzenie aplikacji klienckiej

  1. Otwórz wiersz polecenia i utwórz nowy katalog dla aplikacji. Ustaw to jako bieżący katalog.

  2. Wprowadź następujące polecenie w oknie konsoli:

    dotnet new console --name WebAPIClient
    

    To polecenie tworzy pliki początkowe dla podstawowej aplikacji "Hello World". Nazwa projektu to "WebAPIClient".

  3. Przejdź do katalogu "WebAPIClient" i uruchom aplikację.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run automatycznie uruchamia dotnet restore, aby przywrócić wszystkie zależności wymagane przez aplikację. Uruchamiane jest również dotnet build w razie potrzeby. Powinieneś zobaczyć dane wyjściowe aplikacji "Hello, World!". W terminalu naciśnij Ctrl+C, aby zatrzymać aplikację.

Tworzenie żądań HTTP

Ta aplikacja wywołuje interfejs API GitHub , aby uzyskać informacje o projektach pod zwierzchnictwem .NET Foundation . Punkt końcowy jest https://api.github.com/orgs/dotnet/repos. Aby pobrać informacje, wysyła żądanie HTTP GET. Przeglądarki wysyłają również żądania HTTP GET, aby można było wkleić ten adres URL na pasku adresu przeglądarki, aby zobaczyć, jakie informacje będą odbierane i przetwarzane.

Użyj klasy HttpClient, aby wysyłać żądania HTTP. HttpClient obsługuje tylko metody asynchroniczne dla długotrwałych interfejsów API. Dlatego poniższe kroki umożliwiają utworzenie metody asynchronicznej i wywołanie jej z metody Main.

  1. Otwórz plik Program.cs w katalogu projektu i zastąp jego zawartość następującym kodem:

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

    Ten kod:

    • Zastępuje instrukcję Console.WriteLine wywołaniem ProcessRepositoriesAsync wykorzystującym słowo kluczowe await.
    • Definiuje pustą metodę ProcessRepositoriesAsync.
  2. W klasie Program użyj HttpClient do obsługi żądań i odpowiedzi, zastępując zawartość następującym kodem 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)
    {
    }
    

    Ten kod:

    • Konfiguruje nagłówki HTTP dla wszystkich żądań:
      • Nagłówek Accept akceptujący odpowiedzi JSON
      • Nagłówek User-Agent. Te nagłówki są sprawdzane przez kod serwera GitHub i są niezbędne do pobrania informacji z usługi GitHub.
  3. W metodzie ProcessRepositoriesAsync wywołaj punkt końcowy usługi GitHub, który zwraca listę wszystkich repozytoriów w organizacji platformy .NET Foundation:

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

    Ten kod:

    • Oczekuje na zadanie zwrócone z wywołania metody HttpClient.GetStringAsync(String). Ta metoda wysyła żądanie HTTP GET do określonego adresu URI. Treść odpowiedzi jest zwracana jako String, która jest dostępna po zakończeniu zadania.
    • Ciąg odpowiedzi json jest wypisywany na konsolę.
  4. Skompiluj aplikację i uruchom ją.

    dotnet run
    

    Nie ma ostrzeżenia dotyczącego kompilacji, ponieważ ProcessRepositoriesAsync zawiera teraz operator await. Dane wyjściowe to rozległe wyświetlenie tekstu JSON.

Deserializowanie wyniku JSON

W poniższych krokach przekonwertuj odpowiedź JSON na obiekty języka C#. Klasa System.Text.Json.JsonSerializer służy do deserializacji kodu JSON do obiektów.

  1. Utwórz plik o nazwie Repository.cs i dodaj następujący kod:

    public record class Repository(string name);
    

    Powyższy kod definiuje klasę reprezentującą obiekt JSON zwrócony z interfejsu API usługi GitHub. Ta klasa służy do wyświetlania listy nazw repozytoriów.

    Kod JSON obiektu repozytorium zawiera dziesiątki właściwości, ale tylko właściwość name zostanie zdeserializowana. Serializator automatycznie ignoruje właściwości JSON, dla których w klasie docelowej nie ma dopasowania. Ta funkcja ułatwia tworzenie typów, które działają tylko z podzbiorem pól w dużym pakiecie JSON.

    Konwencja języka C# polega na wielką literą nazw właściwości, ale właściwość name zaczyna się od małej litery, ponieważ odpowiada dokładnie temu, co znajduje się w formacie JSON. Później zobaczysz, jak używać nazw właściwości języka C#, które nie są zgodne z nazwami właściwości JSON.

  2. Użyj serializatora, aby przekonwertować kod JSON na obiekty języka C#. Zastąp wywołanie GetStringAsync(String) w metodzie ProcessRepositoriesAsync następującymi wierszami:

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

    Zaktualizowany kod zastępuje GetStringAsync(String)GetStreamAsync(String). Ta metoda serializatora używa strumienia zamiast ciągu jako źródła.

    Pierwszym argumentem JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) jest wyrażenie await. await wyrażenia mogą pojawiać się niemal wszędzie w kodzie, mimo że do tej pory widziałeś je tylko jako część instrukcji przypisania. Pozostałe dwa parametry, JsonSerializerOptions i CancellationToken, są opcjonalne i pomijane we fragmencie kodu.

    Metoda DeserializeAsync jest ogólne, co oznacza, że dostarczasz argumenty typu określające, jakie obiekty powinny być utworzone na podstawie tekstu JSON. W tym przykładzie deserializujesz do List<Repository>, co jest innym ogólnym obiektem, czyli System.Collections.Generic.List<T>. Klasa List<T> przechowuje kolekcję obiektów. Argument typu deklaruje typ obiektów przechowywanych w List<T>. Typ argumentu to zapis Repository, ponieważ tekst JSON reprezentuje kolekcję obiektów repozytorium.

  3. Dodaj kod, aby wyświetlić nazwę każdego repozytorium. Zastąp linie, które brzmią:

    Console.Write(json);
    

    z następującym kodem:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.Write(repo.name);
    
  4. W górnej części pliku powinny znajdować się następujące dyrektywy using:

    using System.Net.Http.Headers;
    using System.Text.Json;
    
  5. Uruchom aplikację.

    dotnet run
    

    Dane wyjściowe to lista nazw repozytoriów, które są częścią programu .NET Foundation.

Konfigurowanie deserializacji

  1. W Repository.cszastąp zawartość pliku następującym kodem C#.

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

    Ten kod:

    • Zmienia nazwę właściwości name na Name.
    • Dodaje JsonPropertyNameAttribute w celu określenia sposobu wyświetlania tej właściwości w formacie JSON.
  2. W Program.cszaktualizuj kod, aby użyć nowej wielkości liter właściwości Name.

    foreach (var repo in repositories)
       Console.Write(repo.Name);
    
  3. Uruchom aplikację.

    Dane wyjściowe są takie same.

Refaktoryzacja kodu

Metoda ProcessRepositoriesAsync może wykonywać asynchroniczne działanie i zwracać kolekcję repozytoriów. Zmień tę metodę, aby zwracała Task<List<Repository>>. Przenieś kod, który wypisuje na konsolę, w pobliże miejsca wywołania.

  1. Zmień sygnaturę ProcessRepositoriesAsync, aby zwrócić zadanie, którego wynikiem jest lista obiektów Repository:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Zwróć repozytoria po przetworzeniu odpowiedzi JSON:

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

    Kompilator generuje obiekt Task<T> dla wartości zwracanej, ponieważ ta metoda została oznaczona jako async.

  3. Zmodyfikuj plik Program.cs, zastępując wywołanie ProcessRepositoriesAsync następującym kodem, aby przechwycić wyniki i zapisać każdą nazwę repozytorium do konsoli.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.Write(repo.Name);
    
  4. Uruchom aplikację.

    Dane wyjściowe są takie same.

Deserializowanie większej liczby właściwości

Poniższe kroki dodają kod, aby przetworzyć więcej właściwości w odebranych pakietach JSON. Prawdopodobnie nie chcesz przetwarzać każdej właściwości, ale dodanie jeszcze kilku demonstruje inne funkcje języka C#.

  1. Zastąp zawartość klasy Repository następującą definicją record:

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

    Typy Uri i int mają wbudowaną funkcję konwersji na i z reprezentacji ciągu. Do deserializacji z formatu ciągu JSON do tych typów docelowych nie jest potrzebny dodatkowy kod. Jeśli pakiet JSON zawiera dane, które nie są konwertowane na typ docelowy, akcja serializacji zgłasza wyjątek.

  2. Zaktualizuj pętlę foreach w pliku Program.cs, aby wyświetlić wartości właściwości:

    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. Uruchom aplikację.

    Lista zawiera teraz dodatkowe właściwości.

Dodaj właściwość daty

Data ostatniej operacji wypychania jest formatowana w ten sposób w odpowiedzi JSON:

2016-02-08T21:27:00Z

Ten format jest przeznaczony dla uniwersalnego czasu koordynowanego (UTC), dlatego wynikiem deserializacji jest wartość DateTime, której właściwość Kind jest Utc.

Aby uzyskać datę i godzinę reprezentowaną w strefie czasowej, musisz napisać niestandardową metodę konwersji.

  1. W Repository.csdodaj właściwość reprezentującą datę i godzinę w formacie UTC oraz właściwość tylko do odczytu LastPush zwracającą datę przekonwertowaną na czas lokalny, plik powinien wyglądać następująco:

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

    Właściwość LastPush jest zdefiniowana za pomocą elementu członkowskiego wyrażenia dla akcesora get. Nie ma set akcesora. Pominięcie akcesora set jest jednym ze sposobów na zdefiniowanie właściwości tylko do odczytu w języku C#. (Tak, można utworzyć właściwości tylko do zapisu w języku C#, ale ich wartość jest ograniczona).

  2. Dodaj kolejną instrukcję danych wyjściowych w Program.cs: ponownie:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. Kompletna aplikacja powinna przypominać następujący plik Program.cs:

    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. Uruchom aplikację.

    Dane wyjściowe obejmują datę i godzinę ostatniego wypchnięcia do każdego repozytorium.

Następne kroki

W tym samouczku stworzyłeś aplikację, która generuje zapytania sieciowe i przetwarza wyniki. Twoja wersja aplikacji powinna być teraz zgodna z ukończonym przykładem.

Dowiedz się więcej o konfigurowaniu serializacji JSON w Jak serializować i deserializować JSON na platformie .NET.