Compartilhar via


Tutorial: Fazer solicitações HTTP em um aplicativo de console do .NET usando C#

Este tutorial cria um aplicativo que emite solicitações HTTP para um serviço REST no GitHub. O aplicativo lê informações no formato JSON e converte o JSON em objetos C#. A conversão de objetos JSON em C# é conhecida como desserialização.

O tutorial mostra como:

  • Enviar solicitações HTTP.
  • Desserializar respostas JSON.
  • Configure a desserialização com atributos.

Se você preferir acompanhar o exemplo final deste tutorial, poderá baixá-lo. Para obter instruções de download, consulte Exemplos e Tutoriais.

Pré-requisitos

Criar o aplicativo cliente

  1. Abra um prompt de comando e crie um novo diretório para seu aplicativo. Faça disso o diretório atual.

  2. Insira o seguinte comando em uma janela do console:

    dotnet new console --name WebAPIClient
    

    Esse comando cria os arquivos indispiratórios para um aplicativo básico "Olá, Mundo". O nome do projeto é "WebAPIClient".

  3. Navegue até o diretório "WebAPIClient" e execute o aplicativo.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run é executado dotnet restore automaticamente para restaurar as dependências de que o aplicativo precisa. Ele também é executado dotnet build se necessário. Você deve ver a saída "Hello, World!"do aplicativo. No terminal, pressione Ctrl+C para interromper o aplicativo.

Fazer solicitações HTTP

Esse aplicativo chama a API do GitHub para obter informações sobre os projetos no guarda-chuva do .NET Foundation . O ponto de extremidade é https://api.github.com/orgs/dotnet/repos. Para recuperar informações, ele faz uma solicitação HTTP GET. Os navegadores também fazem solicitações HTTP GET, para que você possa colar essa URL na barra de endereços do navegador para ver quais informações você receberá e processará.

Use a HttpClient classe para fazer solicitações HTTP. HttpClient dá suporte apenas a métodos assíncronos para suas APIs de longa execução. Portanto, as etapas a seguir criam um método assíncrono e o chamam do método Main.

  1. Abra o Program.cs arquivo no diretório do projeto e substitua seu conteúdo pelo seguinte:

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

    Este código:

    • Substitui a Console.WriteLine instrução por uma chamada que ProcessRepositoriesAsync usa a await palavra-chave.
    • Define um método vazio ProcessRepositoriesAsync .
  2. Program Na classe, use um HttpClient para lidar com solicitações e respostas, substituindo o conteúdo pelo C#a seguir.

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

    Este código:

    • Configura cabeçalhos HTTP para todas as solicitações:
      • Um Accept cabeçalho para aceitar respostas JSON
      • Um User-Agent cabeçalho. Esses cabeçalhos são verificados pelo código do servidor GitHub e são necessários para recuperar informações do GitHub.
  3. ProcessRepositoriesAsync No método, chame o ponto de extremidade do GitHub que retorna uma lista de todos os repositórios na organização do .NET Foundation:

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

    Este código:

    • Aguarda a tarefa retornada do método de chamada HttpClient.GetStringAsync(String) . Esse método envia uma solicitação HTTP GET para o URI especificado. O corpo da resposta é retornado como um String, que está disponível quando a tarefa é concluída.
    • A cadeia de caracteres de json resposta é impressa no console.
  4. Crie o aplicativo e execute-o.

    dotnet run
    

    Não há nenhum aviso de build porque o ProcessRepositoriesAsync agora contém um await operador. A saída é uma exibição longa do texto JSON.

Desserializar o resultado JSON

As etapas a seguir simplificam a abordagem para buscar os dados e processá-los. Você usará o GetFromJsonAsync método de extensão que faz parte do 📦 pacote NuGet System.Net.Http.Json para buscar e desserializar os resultados JSON em objetos.

  1. Crie um arquivo chamado Repository.cs e adicione o seguinte código:

    public record class Repository(string Name);
    

    O código anterior define uma classe para representar o objeto JSON retornado da API do GitHub. Você usará essa classe para exibir uma lista de nomes de repositório.

    O JSON de um objeto de repositório contém dezenas de propriedades, mas somente a Name propriedade será desserializada. O serializador ignora automaticamente as propriedades JSON para as quais não há correspondência na classe de destino. Esse recurso facilita a criação de tipos que funcionam apenas com um subconjunto de campos em um pacote JSON grande.

    Embora o GetFromJsonAsync método que você usará no próximo ponto tenha o benefício de não diferenciar maiúsculas de minúsculas quando se trata de nomes de propriedade, a convenção C# é para capitalizar a primeira letra de nomes de propriedade.

  2. Use o HttpClientJsonExtensions.GetFromJsonAsync método para buscar e converter JSON em objetos C#. Substitua a chamada no GetStringAsync(String)ProcessRepositoriesAsync método pelas seguintes linhas:

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

    O código atualizado substitui GetStringAsync(String) por HttpClientJsonExtensions.GetFromJsonAsync.

    O primeiro argumento a ser GetFromJsonAsync método é uma expressão await . await as expressões podem aparecer em praticamente qualquer lugar em seu código, mesmo que até agora, você as tenha visto apenas como parte de uma instrução de atribuição. O próximo parâmetro é requestUri opcional e não precisa ser fornecido se já tiver sido especificado ao criar o client objeto. Você não forneceu o objeto com o client URI para o qual enviar a solicitação, portanto, você especificou o URI agora. O último parâmetro opcional, o CancellationToken é omitido no snippet de código.

    O GetFromJsonAsync método é genérico, o que significa que você fornece argumentos de tipo para que tipo de objetos devem ser criados a partir do texto JSON buscado. Neste exemplo, você está desserializando para um List<Repository>, que é outro objeto genérico, um System.Collections.Generic.List<T>. A List<T> classe armazena uma coleção de objetos. O argumento type declara o tipo de objetos armazenados no List<T>. O argumento de tipo é seu Repository registro, pois o texto JSON representa uma coleção de objetos de repositório.

  3. Adicione código para exibir o nome de cada repositório. Substitua as linhas que leem:

    Console.Write(json);
    

    com o seguinte código:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.WriteLine(repo.Name);
    
  4. As seguintes using diretivas devem estar presentes na parte superior do arquivo:

    using System.Net.Http.Headers;
    using System.Net.Http.Json;
    
  5. Execute o aplicativo.

    dotnet run
    

    A saída é uma lista dos nomes dos repositórios que fazem parte do .NET Foundation.

Refatorar o código

O ProcessRepositoriesAsync método pode fazer o trabalho assíncrono e retornar uma coleção de repositórios. Altere esse método para retornar Task<List<Repository>>e mova o código que grava no console perto de seu chamador.

  1. Altere a assinatura para ProcessRepositoriesAsync retornar uma tarefa cujo resultado é uma lista de Repository objetos:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Retorne os repositórios depois de processar a resposta JSON:

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

    O compilador gera o Task<T> objeto para o valor retornado porque você marcou esse método como async.

  3. Modifique o arquivo Program.cs , substituindo a chamada pelo ProcessRepositoriesAsync seguinte para capturar os resultados e gravar cada nome do repositório no console.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
    
  4. Execute o aplicativo.

    A saída é a mesma.

Desserializar mais propriedades

Nas etapas a seguir, estendemos o código para processar mais propriedades do conteúdo JSON retornado pela API do GitHub. Você provavelmente não precisará processar todas as propriedades, mas adicionar algumas demonstra alguns recursos adicionais do C#.

  1. Substitua o conteúdo da Repository classe pela definição a seguir record . Certifique-se de importar o System.Text.Json.Serialization namespace e aplicar o [JsonPropertyName] atributo para mapear campos JSON para propriedades C# explicitamente.

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

    Os Uri tipos e os int tipos têm funcionalidade interna para converter de e para a representação de cadeia de caracteres. Nenhum código extra é necessário para desserializar do formato de cadeia de caracteres JSON para esses tipos de destino. Se o pacote JSON contiver dados que não são convertidos em um tipo de destino, a ação de serialização gerará uma exceção.

    O JSON geralmente usa lowercase ou snake_case para nomes de propriedade. Campos como html_url e pushed_at não seguem as convenções de nomenclatura do C# PascalCase. O uso [JsonPropertyName] garante que essas chaves JSON estejam corretamente associadas às respectivas propriedades C#correspondentes, mesmo quando seus nomes diferem no caso ou contêm sublinhados. Essa abordagem garante a desserialização previsível e estável, permitindo nomes de propriedade PascalCase em C#. Além disso, o GetFromJsonAsync método é case-insensitive ao fazer correspondência de nomes de propriedade, portanto, nenhuma conversão adicional é necessária.

  2. Atualize o foreach loop no arquivo Program.cs para exibir os valores da propriedade:

    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. Execute o aplicativo.

    A lista agora inclui as propriedades adicionais.

Adicionar uma propriedade date

A data da última operação de push é formatada dessa forma na resposta JSON:

2016-02-08T21:27:00Z

Esse formato é para UTC (Tempo Universal Coordenado), portanto, o resultado da desserialização é um DateTime valor cuja Kind propriedade é Utc.

Para obter uma data e hora representadas em seu fuso horário, você precisa escrever um método de conversão personalizado.

  1. Em Repository.cs, adicione uma propriedade para a representação UTC da data e hora e uma propriedade somente leitura LastPush que retorna a data convertida em hora local, o arquivo deve ser semelhante ao seguinte:

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

    A LastPush propriedade é definida usando um membro com corpo de expressão para o get acessador. Não há acessador set . Omitir o set acessador é uma maneira de definir uma propriedade somente leitura em C#. (Sim, você pode criar propriedades somente gravação em C#, mas seu valor é limitado.)

  2. Adicione outra instrução de saída em Program.cs: novamente:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. O aplicativo completo deve ser semelhante ao seguinte arquivo de Program.cs :

    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. Execute o aplicativo.

    A saída inclui a data e a hora do último push para cada repositório.

Próximas etapas

Neste tutorial, você criou um aplicativo que faz solicitações da Web e analisa os resultados. Sua versão do aplicativo agora deve corresponder ao exemplo concluído.

Saiba mais sobre como configurar a serialização JSON em Como serializar e desserializar (marshal e unmarshal) JSON no .NET.