Compartir a través de


Tutorial: Realización de solicitudes HTTP en una aplicación de consola de .NET mediante C#

En este tutorial se compila una aplicación que emite solicitudes HTTP a un servicio REST en GitHub. La aplicación lee información en formato JSON y convierte el JSON en objetos de C#. La conversión de JSON a objetos de C# se conoce como deserialización.

En el tutorial se muestra cómo:

  • Enviar solicitudes HTTP.
  • Deserializar las respuestas JSON.
  • Configure la deserialización con atributos.

Si prefiere seguir el ejemplo final de este tutorial, puede descargarlo. Para obtener instrucciones de descarga, consulte Ejemplos y tutoriales.

Prerrequisitos

Creación de la aplicación cliente

  1. Abra un símbolo del sistema y cree un directorio para la aplicación. Haga que el directorio actual.

  2. Escriba el siguiente comando en una ventana de consola:

    dotnet new console --name WebAPIClient
    

    Este comando crea los archivos de inicio para una aplicación básica "Hola mundo". El nombre del proyecto es "WebAPIClient".

  3. Vaya al directorio "WebAPIClient" y ejecute la aplicación.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run se ejecuta dotnet restore automáticamente para restaurar las dependencias que la aplicación necesita. También se ejecuta dotnet build si es necesario. Debería ver la salida "Hello, World!"de la aplicación. En el terminal, presione Ctrl+C para detener la aplicación.

Realización de solicitudes HTTP

Esta aplicación llama a la API de GitHub para obtener información sobre los proyectos en el paraguas de .NET Foundation . El punto de conexión es https://api.github.com/orgs/dotnet/repos. Para recuperar información, realiza una solicitud HTTP GET. Los exploradores también realizan solicitudes HTTP GET, por lo que puede pegar esa dirección URL en la barra de direcciones del explorador para ver qué información va a recibir y procesar.

Use la HttpClient clase para realizar solicitudes HTTP. HttpClient solo admite métodos asincrónicos para sus API de ejecución prolongada. Por lo tanto, los pasos siguientes crean un método asincrónico y lo llaman desde el método Main.

  1. Abra el archivo en el directorio del Program.cs proyecto y reemplace su contenido por lo siguiente:

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

    Este código:

    • Reemplaza la Console.WriteLine instrucción por una llamada a ProcessRepositoriesAsync que usa la await palabra clave .
    • Define un método vacío ProcessRepositoriesAsync .
  2. En la Program clase , use para HttpClient controlar las solicitudes y respuestas, reemplazando el contenido por el siguiente 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)
    {
    }
    

    Este código:

    • Configura encabezados HTTP para todas las solicitudes:
      • Encabezado Accept para aceptar respuestas JSON
      • Encabezado User-Agent . El código del servidor de GitHub comprueba estos encabezados y son necesarios para recuperar información de GitHub.
  3. En el ProcessRepositoriesAsync método , llame al punto de conexión de GitHub que devuelva una lista de todos los repositorios de la organización de .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:

    • Espera la tarea devuelta desde el método de llamada HttpClient.GetStringAsync(String) . Este método envía una solicitud HTTP GET al URI especificado. El cuerpo de la respuesta se devuelve como , Stringque está disponible cuando se completa la tarea.
    • La cadena json de respuesta se imprime en la consola.
  4. Compile la aplicación y ejecútelo.

    dotnet run
    

    No hay ninguna advertencia de compilación porque ProcessRepositoriesAsync ahora contiene un await operador . La salida es una presentación larga del texto JSON.

Deserializar el resultado JSON

Los pasos siguientes simplifican el enfoque para capturar los datos y procesarlos. Usará el GetFromJsonAsync método de extensión que forma parte del 📦 paquete NuGet System.Net.Http.Json para capturar y deserializar los resultados JSON en objetos.

  1. Cree un archivo denominado Repository.cs y agregue el código siguiente:

    public record class Repository(string Name);
    

    El código anterior define una clase para representar el objeto JSON devuelto desde la API de GitHub. Usará esta clase para mostrar una lista de nombres de repositorio.

    El JSON de un objeto de repositorio contiene docenas de propiedades, pero solo se deserializará la Name propiedad. El serializador omite automáticamente las propiedades JSON para las que no hay ninguna coincidencia en la clase de destino. Esta característica facilita la creación de tipos que funcionan solo con un subconjunto de campos en un paquete JSON grande.

    Aunque el GetFromJsonAsync método que usará en el siguiente punto tiene una ventaja de no distinguir entre mayúsculas y minúsculas cuando se trata de nombres de propiedad, la convención de C# es poner en mayúsculas la primera letra de los nombres de propiedad.

  2. Use el HttpClientJsonExtensions.GetFromJsonAsync método para capturar y convertir JSON en objetos de C#. Reemplace la llamada a GetStringAsync(String) en el ProcessRepositoriesAsync método por las líneas siguientes:

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

    El código actualizado reemplaza GetStringAsync(String) por HttpClientJsonExtensions.GetFromJsonAsync.

    El primer argumento para GetFromJsonAsync el método es una await expresión. await Las expresiones pueden aparecer casi en cualquier parte del código, aunque hasta ahora solo las haya visto como parte de una instrucción de asignación. El siguiente parámetro es requestUri opcional y no tiene que proporcionarse si ya se especificó al crear el client objeto. No proporcionó el client objeto con el URI al que enviar la solicitud, por lo que especificó el URI ahora. El último parámetro opcional, CancellationToken se omite en el fragmento de código.

    El GetFromJsonAsync método es genérico, lo que significa que se proporcionan argumentos de tipo para qué tipo de objetos se deben crear a partir del texto JSON capturado. En este ejemplo, va a deserializar en un List<Repository>objeto , que es otro objeto genérico, un System.Collections.Generic.List<T>. La List<T> clase almacena una colección de objetos . El argumento type declara el tipo de objetos almacenados en .List<T> El argumento type es el Repository registro, ya que el texto JSON representa una colección de objetos de repositorio.

  3. Agregue código para mostrar el nombre de cada repositorio. Reemplace las líneas que leen:

    Console.Write(json);
    

    con el código siguiente:

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.WriteLine(repo.Name);
    
  4. Las siguientes using directivas deben estar presentes en la parte superior del archivo:

    using System.Net.Http.Headers;
    using System.Net.Http.Json;
    
  5. Ejecute la aplicación.

    dotnet run
    

    La salida es una lista de los nombres de los repositorios que forman parte de .NET Foundation.

Refactorización del código

El ProcessRepositoriesAsync método puede realizar el trabajo asincrónico y devolver una colección de los repositorios. Cambie ese método para devolver Task<List<Repository>>y mueva el código que escribe en la consola cerca de su autor de llamada.

  1. Cambie la firma de ProcessRepositoriesAsync para devolver una tarea cuyo resultado es una lista de Repository objetos:

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Devuelve los repositorios después de procesar la respuesta JSON:

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

    El compilador genera el Task<T> objeto para el valor devuelto porque ha marcado este método como async.

  3. Modifique el archivo Program.cs , reemplazando la llamada a ProcessRepositoriesAsync por lo siguiente para capturar los resultados y escribir cada nombre de repositorio en la consola.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
    
  4. Ejecute la aplicación.

    La salida es la misma.

Deserializar más propiedades

En los pasos siguientes, ampliamos el código para procesar más propiedades de la carga JSON devuelta por la API de GitHub. Probablemente no tendrá que procesar todas las propiedades, pero agregar algunas muestran características adicionales de C#.

  1. Reemplace el contenido de la Repository clase por la siguiente record definición. Asegúrese de importar el System.Text.Json.Serialization espacio de nombres y aplicar el [JsonPropertyName] atributo para asignar campos JSON a propiedades de C# explícitamente.

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

    Los Uri tipos y int tienen funcionalidad integrada para convertir a y desde la representación de cadena. No se necesita código adicional para deserializar desde el formato de cadena JSON a esos tipos de destino. Si el paquete JSON contiene datos que no se convierten en un tipo de destino, la acción de serialización produce una excepción.

    JSON suele usar lowercase o snake_case para nombres de propiedad. Los campos como html_url y pushed_at no siguen las convenciones de nomenclatura de PascalCase de C#. El uso de [JsonPropertyName] garantiza que estas claves JSON estén enlazadas correctamente a sus propiedades de C# correspondientes, incluso cuando sus nombres difieren en el uso de mayúsculas o contienen caracteres de subrayado. Este enfoque garantiza la deserialización predecible y estable, al tiempo que permite nombres de propiedad PascalCase en C#. Además, el método GetFromJsonAsync es case-insensitive al coincidir con los nombres de propiedad, por lo que no es necesaria ninguna conversión adicional.

  2. Actualice el foreach bucle en el archivo Program.cs para mostrar los valores de propiedad:

    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. Ejecute la aplicación.

    La lista ahora incluye las propiedades adicionales.

Agregar una propiedad date

La fecha de la última operación de inserción tiene el formato de esta manera en la respuesta JSON:

2016-02-08T21:27:00Z

Este formato es para hora universal coordinada (UTC), por lo que el resultado de la deserialización es un DateTime valor cuya Kind propiedad es Utc.

Para obtener una fecha y hora representadas en la zona horaria, debe escribir un método de conversión personalizado.

  1. En Repository.cs, agregue una propiedad para la representación UTC de la fecha y hora y una propiedad readonly LastPush que devuelva la fecha convertida a la hora local, el archivo debe tener un aspecto similar al siguiente:

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

    La LastPush propiedad se define mediante un miembro con forma de expresión para el descriptor de get acceso. No hay ningún set descriptor de acceso. Omitir el set descriptor de acceso es una manera de definir una propiedad de solo lectura en C#. (Sí, puede crear propiedades de solo escritura en C#, pero su valor es limitado).

  2. Agregue otra instrucción de salida en Program.cs: de nuevo:

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. La aplicación completa debe ser similar al siguiente archivo 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. Ejecute la aplicación.

    La salida incluye la fecha y hora de la última inserción en cada repositorio.

Pasos siguientes

En este tutorial, ha creado una aplicación que realiza solicitudes web y analiza los resultados. La versión de la aplicación debe coincidir ahora con el ejemplo terminado.

Obtenga más información sobre cómo configurar la serialización JSON en Cómo serializar y deserializar (serializar y deserializar) JSON en .NET.