Consumiendo el API en otra APP de Windows Forms - jcpichardo/Progra-II GitHub Wiki

Creación de la App en Windows Forms.

En esta aplicación creen dos carpetas Model y Service.

En model va la clase estudiantes y la clase persona estas son iguales a las que ya creamos en las clases pasadas. Aqui no agregamos la biblioteca de clases por que esta proyecto es la SIMULACIÓN DE OTRO PROYECTO TOTALMENTE INDEPENDIENTE AL NUESTRO.

En la carpeta de Service crean la clase Api Service.

using ControlEscolarCore.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
using System.Threading.Tasks;

public class ApiService
{
    private readonly HttpClient _httpClient;
    // Obtener la URL desde el archivo  configuración
    private readonly string _baseUrl = ConfigurationManager.AppSettings["ApiBaseUrl"] ?? throw new InvalidOperationException("La clave 'ApiBaseUrl' no está configurada en AppSettings.");

    public ApiService()
    {
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(30);   
    }

    public async Task<List<Estudiante>> GetEstudiantesAsync(bool soloActivos = true,
                                                          int tipoFecha = 2,
                                                          DateTime? fechaInicio = null,
                                                          DateTime? fechaFin = null)
    {
        try
        {
            string endpoint = "estudiantescontrollerapi_test/list_estudiantes";
            string queryString = $"?soloActivos={soloActivos}&tipoFecha={tipoFecha}";

            if (fechaInicio.HasValue)
                queryString += $"&fechaInicio={fechaInicio.Value:yyyy-MM-dd}";

            if (fechaFin.HasValue)
                queryString += $"&fechaFin={fechaFin.Value:yyyy-MM-dd}";

            HttpResponseMessage response = await _httpClient.GetAsync(_baseUrl + endpoint + queryString);

            if (response.IsSuccessStatusCode)
            {
                string json = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<List<Estudiante>>(json);
            }
            else
            {
                // Manejar errores
                string errorContent = await response.Content.ReadAsStringAsync();
                throw new Exception($"Error al obtener estudiantes: {response.StatusCode} - {errorContent}");
            }
        }
        catch (Exception ex)
        {
            // Loguear y relanzar la excepción
            Console.WriteLine($"Error en API: {ex.Message}");
            throw;
        }
    }
}

Este clase consumira el API que publicamos

En el archivo de confuración creen una variable para consumir nuestra URL base que por lo general no cambiara y es la base de nuestra API

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<appSettings>
		<add key="ApiBaseUrl" value="https://controlescolar-api-test.onrender.com/api/" />
	</appSettings>
</configuration>

En una ventana de windows forms creen un data grid view y un boton

using System;
using System.Configuration;

namespace ConsumiendoAPI_Test
{
    public partial class Form1 : Form
    {
        private readonly ApiService _apiService;
        public Form1()
        {
            InitializeComponent();
            _apiService = new ApiService();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Consulta();
        }


        private async void Consulta()
        {
            try
            {
                Cursor = Cursors.WaitCursor;
                var estudiantes = await _apiService.GetEstudiantesAsync(true, 2, DateTime.Parse("01/01/2025"), DateTime.Parse("10/05/2025"));

                // Mostrar resultados
                dataGridView1.DataSource = estudiantes;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error al consultar estudiantes: {ex.Message}");
            }
            finally
            {
                Cursor = Cursors.Default;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Consulta();
        }
    }
}

En esta ventana consumimos nuestra clase APIService para poblar la lista de estudiantes

ACTUALIZANDO EL NOMBRE DE UN ESTUDIANTE

  1. En el Controller de Estudiante creamos una funcion especial para solo actualizar el nombre
        public (bool exito, string mensaje) ActualizarNombreEstudiante(int idestudiante, string nuevo_nombre)
        {
            try
            {
 

                // Verificar si el estudiante existe
                Estudiante? estudianteExistente = _estudiantesData.ObtenerEstudiantePorId(idestudiante);
                if (estudianteExistente == null)
                {
                    return (false, $"No se encontró el estudiante con ID {idestudiante}");
                }

                estudianteExistente.DatosPersonales.NombreCompleto = nuevo_nombre;


                bool resultado = _estudiantesData.ActualizarEstudiante(estudianteExistente);

                if (!resultado)
                {
                    _logger.Error($"Error al actualizar el estudiante con ID {idestudiante}");
                    return (false, "Error al actualizar el estudiante en la base de datos");
                }

                _logger.Info($"Estudiante con ID {idestudiante} actualizado exitosamente");
                return (true, "Estudiante actualizado exitosamente");
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"Error inesperado al actualizar estudiante con ID {idestudiante}");
                return (false, $"Error inesperado: {ex.Message}");
            }
        }
  1. En nuestro proyecto WEB API creamos el IActionResult que vamos a exponer
        /// <summary>
        /// Actualiza los datos de un estudiante existente.
        /// </summary>
        /// <param name="idestudiante">Identificador del estudiante</param>
        /// <returns>Resultado de la operación</returns>
        [HttpPut("actualizar_nombreestudiante")]
        public IActionResult ActualizarEstudiante([FromQuery]int idestudiante, [FromQuery] string nuevo_nombre)
        {
            try
            {
                var resultado = _estudiantesController.ActualizarNombreEstudiante(idestudiante, nuevo_nombre);

                if (resultado.exito)
                    return Ok(new { mensaje = resultado.mensaje });
                else
                    return BadRequest(new { mensaje = resultado.mensaje });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al actualizar estudiante");
                return StatusCode(500, "Error interno del servidor: " + ex.Message);
            }
        }
  1. Revisen bien que ahora es [HttpPut("actualizar_nombreestudiante")]
  2. En la anterior funcion fue GET por que obtenemos datos, pero si queremos insertar o acutualizar la funcion es PUT
  3. Compilar, hacer un commit en GIT y hacen Deploy del API en Render
  4. Creen una nueva ventana del proyecto en Windows Forms que tenemos por separado y llamen a la nueva fuincion del API.

NOTA yo use el ID para mostrarles el ejemplo rapido, ustedes pueden usar el numero de matricula.

  1. Codigo de consumo en APISerivice
    public async Task<(bool exito, string mensaje)> ActualizarEstudianteAsync(int idestudiante, string nuevo_nombre)
    {
        try
        {
            // Asegurarse de codificar el nombre correctamente en la URL
            string nuevoNombreEncoded = Uri.EscapeDataString(nuevo_nombre);

            // Construir la URL con ambos parámetros en query string
            string endpoint = $"estudiantescontrollerapi_test/actualizar_nombreestudiante?idestudiante={idestudiante}&nuevo_nombre={nuevoNombreEncoded}";

            // Enviar PUT sin contenido en el body
            var request = new HttpRequestMessage(HttpMethod.Put, _baseUrl + endpoint);

            HttpResponseMessage response = await _httpClient.SendAsync(request);

            string responseContent = await response.Content.ReadAsStringAsync();

            if (response.IsSuccessStatusCode)
            {
                var resultado = JsonConvert.DeserializeObject<dynamic>(responseContent);
                return (true, resultado?.mensaje?.ToString() ?? "Estudiante actualizado con éxito");
            }
            else
            {
                var resultado = JsonConvert.DeserializeObject<dynamic>(responseContent);
                string mensajeError = resultado?.mensaje?.ToString() ?? "Error al actualizar estudiante";
                return (false, mensajeError);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error en API al actualizar estudiante: {ex.Message}");
            return (false, $"Error inesperado: {ex.Message}");
        }
    }
  1. Consumo en Windows Forms
        public async void LlamarActualizarEstudiante()
        {
            int id = int.Parse(txtID.Text); // Suponiendo que tienes un TextBox llamado txtIdEstudiante
            string nuevoNombre = txtNuevoNombre.Text; // Suponiendo que tienes un TextBox llamado txtNuevoNombre

            var resultado = await _apiService.ActualizarEstudianteAsync(id, nuevoNombre); // Servicio donde tienes el método async

            if (resultado.exito)
            {
                MessageBox.Show(resultado.mensaje, "Éxito", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                MessageBox.Show(resultado.mensaje, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
⚠️ **GitHub.com Fallback** ⚠️