Samouczek: tworzenie żądań HTTP w aplikacji konsolowej .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 deserializacji.

W samouczku pokazano, jak:

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

Jeśli wolisz korzystać z przykładu końcowego na potrzeby tego samouczka, możesz go pobrać. Aby uzyskać instrukcje dotyczące pobierania, zobacz Przykłady i samouczki.

Wymagania wstępne

  • Zestaw .NET SDK 6.0 lub nowszy
  • Edytor kodu, taki jak [Visual Studio Code (edytor typu open source, międzyplatformowy). Przykładową aplikację można uruchomić w systemie Windows, Linux lub macOS albo w kontenerze platformy Docker.

Tworzenie aplikacji klienckiej

  1. Otwórz wiersz polecenia i utwórz nowy katalog dla aplikacji. Ustaw 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 program automatycznie uruchamia polecenie dotnet restore w celu przywrócenia wszelkich zależności wymaganych przez aplikację. Jest ona również uruchamiana dotnet build w razie potrzeby. Powinny zostać wyświetlone dane wyjściowe "Hello, World!"aplikacji . W terminalu naciśnij klawisze Ctrl+C , aby zatrzymać aplikację.

Zgłaszanie żądań HTTP

Ta aplikacja wywołuje interfejs API usługi GitHub , aby uzyskać informacje o projektach w ramach parasola .NET Foundation . Punkt końcowy to https://api.github.com/orgs/dotnet/repos. Aby pobrać informacje, wysyła żądanie HTTP GET. Przeglądarki również wysyłają żą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.

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

  1. Program.cs Otwórz plik 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 , które ProcessRepositoriesAsync używa słowa kluczowego await .
    • Definiuje pustą ProcessRepositoriesAsync metodę.
  2. Program W klasie użyj elementu HttpClient , aby obsługiwać żądania 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 do akceptowania 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 fundacji platformy .NET:

     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 metody wywołującej HttpClient.GetStringAsync(String) . Ta metoda wysyła żądanie HTTP GET do określonego identyfikatora URI. Treść odpowiedzi jest zwracana jako Stringelement , który jest dostępny po zakończeniu zadania.
    • Ciąg json odpowiedzi jest drukowany w konsoli.
  4. Skompiluj aplikację i uruchom ją.

    dotnet run
    

    Nie ma ostrzeżenia dotyczącego kompilacji, ponieważ ProcessRepositoriesAsync element zawiera await teraz operator . Dane wyjściowe są długim wyświetlaniem tekstu JSON.

Deserializowanie wyniku JSON

W poniższych krokach przekonwertuj odpowiedź JSON na obiekty języka C#. Klasa służy System.Text.Json.JsonSerializer do deserializacji danych 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 name właściwość zostanie zdeserializowana. Serializator automatycznie ignoruje właściwości JSON, dla których nie ma dopasowania w klasie docelowej. 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ę pierwszych nazw właściwości, ale name właściwość zaczyna się od małej litery, ponieważ odpowiada dokładnie temu, co znajduje się w kodzie 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 metody 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 element GetStringAsync(String) .GetStreamAsync(String) Ta metoda serializatora używa strumienia zamiast ciągu jako źródła.

    Pierwszym argumentem await jest JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) wyrażenie. await Wyrażenia mogą pojawiać się niemal w dowolnym miejscu w kodzie, mimo że do tej pory zostały one widoczne tylko w ramach instrukcji przypisania. Pozostałe dwa parametry JsonSerializerOptions i CancellationToken, są opcjonalne i są pomijane we fragmencie kodu.

    Metoda DeserializeAsync jest ogólna, co oznacza, że należy podać argumenty typu dla tego rodzaju obiektów, które powinny zostać utworzone na podstawie tekstu JSON. W tym przykładzie deserializujesz obiekt List<Repository>, który jest innym obiektem ogólnym , czyli System.Collections.Generic.List<T>. Klasa List<T> przechowuje kolekcję obiektów. Argument type deklaruje typ obiektów przechowywanych w obiekcie List<T>. Argument type jest Repository rekordem, ponieważ tekst JSON reprezentuje kolekcję obiektów repozytorium.

  3. Dodaj kod, aby wyświetlić nazwę każdego repozytorium. Zastąp wiersze odczytane:

    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 using dyrektywy:

    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 pliku Repository.cs zastą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ę name właściwości na Name.
    • Dodaje element , JsonPropertyNameAttribute aby określić sposób wyświetlania tej właściwości w formacie JSON.
  2. W pliku Program.cs zaktualizuj kod, aby używał nowego wielkich liter Name właściwości:

    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ć zadania asynchroniczne i zwracać kolekcję repozytoriów. Zmień tę metodę, aby zwracała Task<List<Repository>>metodę , i przenieś kod, który zapisuje w konsoli w pobliżu elementu wywołującego.

  1. Zmień podpis , ProcessRepositoriesAsync aby zwrócić zadanie, którego wynik jest listą Repository obiektów:

    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 Task<T> obiekt dla wartości zwracanej, ponieważ ta metoda została oznaczona jako async.

  3. Zmodyfikuj plik Program.cs , zastępując wywołanie ProcessRepositoriesAsync metody na następującą, aby przechwycić wyniki i zapisać każdą nazwę repozytorium w 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 kilku kolejnych demonstruje inne funkcje języka C#.

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

    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ę konwertowania na ciąg 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.

Dodawanie właściwości date

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 DateTime wartość, której Kind właściwość to Utc.

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

  1. W pliku Repository.cs dodaj właściwość dla reprezentacji CZASU UTC daty i godziny oraz właściwości readonly LastPush , która zwraca 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ść jest definiowana LastPush przy użyciu elementu członkowskiego typu wyrażenie-bodied dla get metody dostępu. Nie ma set akcesorium. Pominięcie set metody dostępu jest jednym ze sposobów definiowania właściwości tylko do odczytu w języku C#. (Tak, można tworzyć właściwości tylko do zapisu w języku C#, ale ich wartość jest ograniczona).

  2. Dodaj kolejną instrukcję wyjściową w pliku 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 utworzono aplikację, która wysyła żądania internetowe i analizuje wyniki. Twoja wersja aplikacji powinna być teraz zgodna z gotowym przykładem.

Dowiedz się więcej o sposobie konfigurowania serializacji JSON w temacie Jak serializować i deserializować (marshaling i unmarshal) JSON na platformie .NET.