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
Otwórz wiersz polecenia i utwórz nowy katalog dla aplikacji. Ustaw bieżący katalog.
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".
Przejdź do katalogu "WebAPIClient" i uruchom aplikację.
cd WebAPIClient
dotnet run
dotnet run
program automatycznie uruchamia poleceniedotnet restore
w celu przywrócenia wszelkich zależności wymaganych przez aplikację. Jest ona również uruchamianadotnet 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.
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óreProcessRepositoriesAsync
używa słowa kluczowegoawait
. - Definiuje pustą
ProcessRepositoriesAsync
metodę.
- Zastępuje instrukcję
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.
- Nagłówek
- Konfiguruje nagłówki HTTP dla wszystkich żądań:
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.
Skompiluj aplikację i uruchom ją.
dotnet run
Nie ma ostrzeżenia dotyczącego kompilacji, ponieważ
ProcessRepositoriesAsync
element zawieraawait
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.
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.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 parametryJsonSerializerOptions
iCancellationToken
, 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 obiektList<Repository>
, który jest innym obiektem ogólnym , czyli System.Collections.Generic.List<T>. KlasaList<T>
przechowuje kolekcję obiektów. Argument type deklaruje typ obiektów przechowywanych w obiekcieList<T>
. Argument type jestRepository
rekordem, ponieważ tekst JSON reprezentuje kolekcję obiektów repozytorium.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);
W górnej części pliku powinny znajdować się następujące
using
dyrektywy:using System.Net.Http.Headers; using System.Text.Json;
Uruchom aplikację.
dotnet run
Dane wyjściowe to lista nazw repozytoriów, które są częścią programu .NET Foundation.
Konfigurowanie deserializacji
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 naName
. - Dodaje element , JsonPropertyNameAttribute aby określić sposób wyświetlania tej właściwości w formacie JSON.
- Zmienia nazwę
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);
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.
Zmień podpis ,
ProcessRepositoriesAsync
aby zwrócić zadanie, którego wynik jest listąRepository
obiektów:static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
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 jakoasync
.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);
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#.
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.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(); }
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.
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 dlaget
metody dostępu. Nie maset
akcesorium. Pominięcieset
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).Dodaj kolejną instrukcję wyjściową w pliku Program.cs: ponownie:
Console.WriteLine($"Last push: {repo.LastPush}");
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(); }
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.