API - jcpichardo/Progra-II GitHub Wiki

Separación del proyecto

Actualmente tenemos un proyecto de Windows Forms, el cual no es compatible con un proyecto Web y debemos considerar que dentro de nuestros controller tenemos las funciones que debemos exponer públicamente en la web.

Comenzaremos con separar el proyecto de la siguiente manera:

  1. Aplicación Windows Forms
  2. Biblioteca de clases (todo el backend, Controllers, Data, PostgresSQLDataAcces)

Nuestra aplicación windows forms, ahora consumirá todo a través de esta biblioteca de clases

  1. Agregamos un proyecto de biblioteca de clases

image

  1. Lo llamaremos ControlEscolarCore

image

  1. Borramos la clase predeterminada que se creo.

image

  1. Vamos a mover los siguientes capas a ControlEscolarCore

image

Nuesto proyecto ahora debe quedar estructurado de la siguiente manera:

image

ControlEscolarCore es nuestra biblioteca de clases y aqui tenemos todo el backend, exportable a cualquier proyecto.

Recompilamos la solución y vemos que tenemos muchos errores ya que ahora tenemos que llamar todo el backend de la biblioteca de clases

image

Muchos errores de la compilación son porque nuestro proyecto ControlEscolarCore no tiene instalados los paquetes Nuget.

image

El siguiente paso sera cambiar el nombre del namespace a todas las clases de nuestra biblioteca de clases image

Apuntamos a los nuevos nombres y compilamos nuestra biblioteca de clases.

EL OBJETIVO ES COMPILAR SIN ERRORES NUESTRA BILBLIOTECA DE CLAES PARA LLAMARLA EN NUESTRO PROYECTO EN LA VISTA

Un error importante se da tambien al no detectar el objeto ConfigurationManager

image

Instalemos el nuget en el proyecto.

image

CON ESTO QUEDA COMPILADO DE MANERA CORRECTA NUESTA BIBLIOTECA DE CLASES

Intentamos compilar la aplicación Widows Forma y observamos que tiene ERRORES por que no existe ningun objeto del backend

Para resolver este problema, realizaremos lo siguiente

  1. Incluiremos como referencia nuestra biblioteca de clases.
image
image
image
  1. Renombrar los Using
image
  1. Las clases que son Internal tendran que cambiar a Public
image
  1. Corregir todos los errores que se presenten apuntando a la biblioteca de clases.

LA APLICACIÓN DEBE CORRER Y FUNCIONAR DE MANERA CORRECTA ESTRUCTURADA DE LA SIGUIENTE MANERA.

image

Creando el WEB API de nuestro proyecto.

  1. Creamos un nuevo proyecto dentro de nuestra solución
image
  1. Creamos un proyecto de Web API
image
  1. Colocamos un nombre
image
image
  1. Agregamos la clase del controlador API que contendra las funciones que vamos a exponer en la WEB
image
  1. Nuestro nuevo controlador nos quedo de la siguiente manera.
image
  1. Al igual que en nuestro proyecto de Windows Form, en este proyecto WEB tambien agregaremos una referencia al proyecto, y será nuestra biblioteca de clases.
image
  1. Buscamos la biblioteca de clases
image
  1. El proyecto debe de quedar de la siguiente manera
image
  1. Agregamos los namespace que ocuparemos en el API WEB, asi como atributos que definiran el comportamiento de nuestra WEB API
image
  1. Declaramos dos objetos
image
  1. Declaramos el constructor
        public EstudiantesControllerAPI_test(EstudiantesController estudiantesController, ILogger<EstudiantesControllerAPI_test> logger)
        {
            _estudiantesController = estudiantesController;
            _logger = logger;
        }
  1. En nuestra clase heredaremos de ControllerBase que ya nos brinda respuestas HTTP
public class EstudiantesControllerAPI_test : ControllerBase
image
image
  1. Finalmente creamos nuestra función que devolvera los estudiantes, trabajando con los controllers que hicimos en nuestro proyecto de la biblioteca de clases.
image

Publicar el API en Render

  1. Agregar dos archivos necesarios para publicar en Render.

  2. .dockerignore

image
  1. Agregamos un nuevo elemento al proyecto, seleccionamos archivo de texto y lo nombramos tal cual esta en el paso 2.
# Archivos y carpetas generales a ignorar
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/obj
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/secrets.dev.yaml
**/values.dev.yaml
README.md

# Ignorar específicamente el proyecto Windows Forms
ControlEscolar/
ControlEscolar.Tests/
# Cualquier otro proyecto específico de Windows que no sea necesario para la API

# Ignorar archivos de desarrollo
**/*.user
**/*.suo

  1. Dockerfile

  2. Abrimos la carpeta donde se encuentra la solución del proyecto.

  3. Creamos un archivo sin extensión llamado Dockerfile, abrimos un notepad y creamos un archivo con este nombre entre comillas

Contenido del archivo

# Etapa de construcción
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app

# Copiar toda la solución primero
COPY . ./

# Listar los directorios para depuración
RUN ls -la

# Intentar construir solo el proyecto API
RUN find . -name "API_Estudiantes_Test.csproj" -exec dotnet publish {} -c Release -o /app/out \;

# Si lo anterior falla, intentar buscar la API por nombre
RUN if [ ! -d /app/out ]; then \
    find . -name "*.csproj" | grep -i api | xargs -I {} dotnet publish {} -c Release -o /app/out; \
    fi

# Etapa final
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/out .
EXPOSE 80
EXPOSE 443

# Buscar el nombre exacto del DLL de la API
RUN find . -name "*.dll" | grep -i api

# Entrypoint dinámico que busca el archivo API.dll
ENTRYPOINT ["sh", "-c", "dotnet $(find . -name \"*API.dll\" | head -1)"]
  1. Subir el proyecto del Web Api a Git con la misma cuenta que tenemos el registro en render

  2. Abrimos nuestra cuenta en Render y creamos un nuevo proyecto de tipo WS

image
  1. Seleccionamos nuestro proyecto WEB API Net Core que subimos en Git
image
  1. Configuramos de la siguiente manera
image
  1. Seleccionamos el plan gratuito
image
  1. Creamos una variable de entorno para la cadena de conexión
ConnectionStrings__DefaultConnection
ConnectionStrings__DefaultConnection=Host=XXXX;Port=5432;Database=XXXX;Username=XXXX;Password=XXXX;SSL Mode=Require;Trust Server Certificate=true;
image
  1. Hacemos el Deploy de nuestro WS
image

Probando en POSTMAN

  1. Generamos una colección en blanco
image
  1. Colocan un nombre
image
  1. Añadimos un request
image
  1. Colocamos un nombre
image
  1. Generamos el URL de la petición, verifiquen que sea GET la petición HTTP
image

https://controlescolar-api-test.onrender.com/api/estudiantescontrollerapi_test/list_estudiantes?soloActivos=true&tipoFecha=2&fechaInicio=2025-01-01&fechaFin=2025-05-09 Hacemos la petición, obtendremos un error 500

Para observar los errores vayamos a los LOGS de render para identificar el error

Creando la inyección de dependencias y modificando el constructor de la clase PostgreSQLDataAcces

  1. En el Program del API indicaremos nuestra dependencias del proyecto.
using ControlEscolarCore.Controller;
using ControlEscolarCore.Data;
// Configurar la cadena de conexión para PostgreSQLDataAccess
PostgreSQLDataAccess.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
//Agregamos el controller de Estudiantes
builder.Services.AddScoped<EstudiantesController>();

El Program del API queda de la siguiente manera:

using ControlEscolarCore.Controller;
using ControlEscolarCore.Data;


var builder = WebApplication.CreateBuilder(args);

// Configurar la cadena de conexión para PostgreSQLDataAccess
PostgreSQLDataAccess.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//Agregamos el controller de Estudiantes
builder.Services.AddScoped<EstudiantesController>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();
  1. Tenemos que hacer algunas modificaciones en el PostgreSQLDataAccess para que no solo inicie con nuestra cadena de conexión del proyecto Windows Forms, si no que tambien pueda iniciar con la que tenemos en el API

Comentamos la inicialización

 // Cadena de conexión desde App.Config
 //private static readonly string _ConnectionString = ConfigurationManager.ConnectionStrings["ConexionBD"].ConnectionString;
 // Campo estático para almacenar la cadena de conexión
 private static string _connectionString;
  // Propiedad para establecer la cadena de conexión desde el API
  public static string ConnectionString
  {
      get
      {
          if (string.IsNullOrEmpty(_connectionString))
          {
              try
              {
                  // Intenta obtener desde ConfigurationManager (Windows Forms)
                  _connectionString = ConfigurationManager.ConnectionStrings["ConexionBD"]?.ConnectionString;
              }
              catch (Exception ex)
              {
                  _logger.Warn(ex, "No se pudo obtener la cadena de conexión desde ConfigurationManager");
              }
          }
          return _connectionString;
      }
      set { _connectionString = value; }
  }

Comentamos el constructor ya que no nos funciona del todo

        ///// <summary>
        ///// Constructor privado para implementar el patrón Singleton.
        ///// </summary>
        //private PostgreSQLDataAccess()
        //{
        //    try
        //    {
        //        _connection = new NpgsqlConnection(_ConnectionString);
        //        _logger.Info("Instancia de acceso a datos creada correctamente");
        //    }
        //    catch (Exception ex)
        //    {
        //        _logger.Fatal(ex, "Error al inicializar el acceso a la base de datos");
        //        throw;
        //    }
        //}

Nuevo constructor

        private PostgreSQLDataAccess()
        {
            try
            {
                if (string.IsNullOrEmpty(ConnectionString))
                {
                    throw new InvalidOperationException("La cadena de conexión no está configurada. Asegúrate de establecer PostgreSQLDataAccess.ConnectionString antes de usar la clase.");
                }

                _connection = new NpgsqlConnection(ConnectionString);
                _logger.Info("Instancia de acceso a datos creada correctamente");
            }
            catch (Exception ex)
            {
                _logger.Fatal(ex, "Error al inicializar el acceso a la base de datos");
                throw;
            }
        }
  1. Generamos versión

  2. Subimos versión a GIT

  3. Publicamos en Render

  4. Probamos de nuevo en POSTMAN

⚠️ **GitHub.com Fallback** ⚠️