이 자습서에서는 GitHub의 REST 서비스에 대한 HTTP 요청을 발급하는 앱을 빌드합니다. 앱은 JSON 형식으로 정보를 읽고 JSON을 C# 개체로 변환합니다. JSON에서 C# 개체로 변환하는 것을 역직렬화라고 합니다.
이 자습서에서는 다음 방법을 보여줍니다.
- HTTP 요청을 보냅니다.
- JSON 응답을 역직렬화합니다.
- 특성을 사용하여 역직렬화를 구성합니다.
이 자습서의 마지막 샘플 과 함께 수행하려는 경우 다운로드할 수 있습니다. 다운로드 지침은 샘플 및 자습서참조하세요.
필수 조건
- 최신 .NET SDK
- Visual Studio Code 편집기
- C# 개발 키트
클라이언트 앱 만들기
명령 프롬프트를 열고 앱에 대한 새 디렉터리를 만듭니다. 현재 디렉터리로 설정하십시오.
콘솔 창에서 다음 명령을 입력합니다.
dotnet new console --name WebAPIClient
이 명령은 기본 "Hello World" 앱에 대한 시작 파일을 만듭니다. 프로젝트 이름은 "WebAPIClient"입니다.
"WebAPIClient" 디렉터리로 이동하여 앱을 실행합니다.
cd WebAPIClient
dotnet run
dotnet run
는 앱에 필요한 모든 종속성을 복원하기 위해 자동으로 실행됩니다dotnet restore
. 필요한 경우dotnet build
이(가) 실행되기도 합니다. 앱 출력"Hello, World!"
이 표시됩니다. 터미널에서 Ctrl+C 를 눌러 앱을 중지합니다.
HTTP 요청하기
이 앱은 GitHub API 를 호출하여 .NET Foundation 우산 아래의 프로젝트에 대한 정보를 가져옵니다. 엔드포인트는 .입니다 https://api.github.com/orgs/dotnet/repos. 정보를 검색하기 위해 HTTP GET 요청을 만듭니다. 또한 브라우저는 HTTP GET 요청을 하므로 해당 URL을 브라우저 주소 표시줄에 붙여넣어 수신 및 처리할 정보를 확인할 수 있습니다.
클래스를 HttpClient 사용하여 HTTP 요청을 만듭니다. HttpClient 는 장기 실행 API에 대한 비동기 메서드만 지원합니다. 따라서 다음 단계에서는 비동기 메서드를 만들고 Main 메서드에서 호출합니다.
Program.cs
프로젝트 디렉터리에서 파일을 열고 해당 내용을 다음으로 바꿉니다.await ProcessRepositoriesAsync(); static async Task ProcessRepositoriesAsync(HttpClient client) { }
이 코드:
-
Console.WriteLine
문을await
키워드를 사용하는ProcessRepositoriesAsync
호출로 바꿉니다. - 빈
ProcessRepositoriesAsync
메서드를 정의합니다.
-
Program
클래스에서 HttpClient을 사용하여, 다음 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) { }
이 코드:
- 모든 요청에 대해 HTTP 헤더를 설정합니다.
-
Accept
JSON 응답을 수락하는 헤더 -
User-Agent
헤더입니다. 이러한 헤더는 GitHub 서버 코드에서 확인되며 GitHub에서 정보를 검색하는 데 필요합니다.
-
- 모든 요청에 대해 HTTP 헤더를 설정합니다.
ProcessRepositoriesAsync
메서드에서 .NET Foundation 조직 아래의 모든 리포지토리 목록을 반환하는 GitHub 엔드포인트를 호출합니다.static async Task ProcessRepositoriesAsync(HttpClient client) { var json = await client.GetStringAsync( "https://api.github.com/orgs/dotnet/repos"); Console.Write(json); }
이 코드:
- 호출 HttpClient.GetStringAsync(String) 메서드에서 반환된 작업을 기다립니다. 이 메서드는 HTTP GET 요청을 지정된 URI로 보냅니다. 응답 본문은 작업이 완료되면 String로 반환됩니다.
- 응답 문자열
json
이 콘솔에 인쇄됩니다.
앱을 빌드하고 실행합니다.
dotnet run
await
연산자가 이제ProcessRepositoriesAsync
에 포함되어 있으므로 빌드 경고가 없습니다. 출력은 JSON 텍스트의 긴 표시입니다.
JSON 결과 역직렬화
다음 단계에서는 JSON 응답을 C# 개체로 변환합니다. 클래스를 System.Text.Json.JsonSerializer 사용하여 JSON을 개체로 역직렬화합니다.
Repository.cs 파일을 만들고 다음 코드를 추가합니다.
public record class Repository(string name);
앞의 코드는 GitHub API에서 반환된 JSON 개체를 나타내는 클래스를 정의합니다. 이 클래스를 사용하여 리포지토리 이름 목록을 표시합니다.
리포지토리 개체의 JSON에는 수십 개의 속성이
name
포함되지만 속성만 역직렬화됩니다. serializer는 대상 클래스에 일치하는 항목이 없는 JSON 속성을 자동으로 무시합니다. 이 기능을 사용하면 큰 JSON 패킷에 있는 필드의 하위 집합만 사용하는 형식을 더 쉽게 만들 수 있습니다.C# 규칙은 속성 이름의 첫 글자를 대문자로 하는 것이지만
name
, 여기서 속성은 JSON의 내용과 정확히 일치하기 때문에 소문자로 시작합니다. 나중에 JSON 속성 이름과 일치하지 않는 C# 속성 이름을 사용하는 방법을 확인할 수 있습니다.serializer를 사용하여 JSON을 C# 개체로 변환합니다.
ProcessRepositoriesAsync
메서드에서 GetStringAsync(String)의 호출을 다음 줄로 대체하십시오.await using Stream stream = await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos"); var repositories = await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
업데이트된 코드는 GetStringAsync(String)을 GetStreamAsync(String)으로 바꿉니다. 이 serializer 메서드는 문자열 대신 스트림을 원본으로 사용합니다.
첫 번째 인수 JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)는
await
식입니다.await
표현식은 코드 내 거의 모든 위치에 나타날 수 있지만, 지금까지는 할당문에서만 본 적이 있을 것입니다. 다른 두 매개 변수인JsonSerializerOptions
및CancellationToken
은 선택 사항이며 코드 조각에서 생략되어 있습니다.이 메서드는
DeserializeAsync
제네릭입니다. 즉, JSON 텍스트에서 만들어야 하는 개체의 종류에 대한 형식 인수를 제공합니다. 이 예제에서는List<Repository>
를 제네릭 개체로 역직렬화하고, 이는 또 다른 제네릭 개체인 System.Collections.Generic.List<T>입니다. 클래스는List<T>
개체 컬렉션을 저장합니다. 형식 인수는 에 저장된 개체의 형식을List<T>
선언합니다. JSON 텍스트는Repository
리포지토리 개체의 컬렉션을 나타내므로 형식 인수는 해당 레코드입니다.각 리포지토리의 이름을 표시하는 코드를 추가합니다. 다음과 같이 읽히는 줄을 교체합니다.
Console.Write(json);
다음 코드를 사용합니다.
foreach (var repo in repositories ?? Enumerable.Empty<Repository>()) Console.Write(repo.name);
다음
using
지시문은 파일 맨 위에 있어야 합니다.using System.Net.Http.Headers; using System.Text.Json;
앱을 실행합니다.
dotnet run
출력은 .NET Foundation의 일부인 리포지토리의 이름 목록입니다.
역직렬화 설정
Repository.cs 파일 내용을 다음 C#으로 바꿉니다.
using System.Text.Json.Serialization; public record class Repository( [property: JsonPropertyName("name")] string Name);
이 코드:
- 속성
Name
이름을name
.로 변경합니다. - JSON에 JsonPropertyNameAttribute 이 속성이 표시되는 방식을 지정하는 데 추가합니다.
- 속성
Program.cs 속성의 새 대문자를 사용하도록 코드를 업데이트합니다
Name
.foreach (var repo in repositories) Console.Write(repo.Name);
앱을 실행합니다.
출력은 동일합니다.
코드 리팩터링
메서드는 ProcessRepositoriesAsync
비동기 작업을 수행하고 리포지토리의 컬렉션을 반환할 수 있습니다. 해당 메서드를 Task<List<Repository>>
을 반환하도록 변경하고, 그 호출자 근처로 콘솔에 쓰는 코드를 옮기세요.
ProcessRepositoriesAsync
의 서명을Repository
개체 목록 결과를 반환하는 작업으로 변경합니다.static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
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();
컴파일러는 이 메서드를
async
로 표시했으므로 반환 값의Task<T>
개체를 생성합니다.Program.cs 파일을 수정하고 호출을
ProcessRepositoriesAsync
다음으로 바꿔 결과를 캡처하고 각 리포지토리 이름을 콘솔에 씁니다.var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) Console.Write(repo.Name);
앱을 실행합니다.
출력은 동일합니다.
더 많은 속성 역직렬화
다음 단계에서는 받은 JSON 패킷에서 더 많은 속성을 처리하는 코드를 추가합니다. 모든 속성을 처리하고 싶지는 않지만 몇 가지를 더 추가하면 C#의 다른 기능이 표시됩니다.
클래스의
Repository
내용을 다음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);
Uri 및
int
형식에는 문자열 표현으로 변환하거나 문자열 표현으로부터 변환할 수 있는 기본 제공 기능이 있습니다. JSON 문자열 형식에서 해당 대상 형식으로 역직렬화하는 데 추가 코드가 필요하지 않습니다. JSON 패킷에 대상 형식으로 변환되지 않는 데이터가 포함된 경우 serialization 작업은 예외를 throw합니다.foreach
Program.cs 파일의 루프를 업데이트하여 속성 값을 표시합니다.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(); }
앱을 실행합니다.
이제 목록에 추가 속성이 포함됩니다.
날짜 속성 추가
마지막 푸시 작업의 날짜는 JSON 응답에서 다음과 같이 형식이 지정됩니다.
2016-02-08T21:27:00Z
이 형식은 협정 세계시(UTC)를 위한 것이므로, 역직렬화의 결과는 DateTime이며, 이 값의 Kind 속성은 Utc입니다.
표준 시간대에 표시되는 날짜 및 시간을 얻으려면 사용자 지정 변환 방법을 작성해야 합니다.
Repository.cs 날짜 및 시간의 UTC 표현에 대한 속성과 현지 시간으로 변환된 날짜를 반환하는 읽기 전용
LastPush
속성을 추가하면 파일은 다음과 같이 표시됩니다.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(); }
LastPush
속성은 식 본문 멤버를 사용하여get
접근자를 정의합니다.set
접근자가 없습니다. 접근자를set
생략하는 것은 C#에서 읽기 전용 속성을 정의하는 한 가지 방법입니다. (예, C#에서 쓰기 전용 속성을 만들 수 있지만 해당 값은 제한됩니다.)Program.cs에 다시 출력 문을 추가합니다.
Console.WriteLine($"Last push: {repo.LastPush}");
전체 앱은 다음 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(); }
앱을 실행합니다.
출력에는 각 리포지토리에 대한 마지막 푸시의 날짜와 시간이 포함됩니다.
다음 단계
이 자습서에서는 웹 요청을 만들고 결과를 구문 분석하는 앱을 만들었습니다. 이제 앱 버전이 완성된 샘플과 일치해야 합니다.
.NET에서 JSON을 직렬화 및 역직렬화(마샬링 및 언마샬링)하는 방법을 구성하는 방법에 대해 자세히 알아봅니다.
.NET