// Servidor minimo de ejemplo (ASP.NET Core minimal API) para las tablas de Ramaflex: // GET | PUT | DELETE {RAMAFLEX_RUTA_BASE}/{TABLA} // Solo autentica y llama al nucleo (TablasRamaflex). En su API, la ruta se monta igual dentro de lo que ya tengan. // // Compilado y probado con .NET 8 contra SQL Server, PostgreSQL y MySQL (ver LEEME.txt). using System.Security.Cryptography; using System.Text; using Ramaflex; var app = WebApplication.CreateBuilder(args).Build(); var motor = Variable("RAMAFLEX_MOTOR"); // sqlserver | postgresql | mysql // El catalogo de las tablas no se configura: el nucleo lo lee de la base (XESQUEMA.CATALOGO). var tablas = new TablasRamaflex( motor, Dialecto.Conexion(motor, Variable("RAMAFLEX_CONEXION")), e => app.Logger.LogError(e, "Error interno en las tablas de Ramaflex")); var credencial = Encoding.UTF8.GetBytes("Bearer " + Variable("RAMAFLEX_TOKEN")); var rutaBase = "/" + Variable("RAMAFLEX_RUTA_BASE", "/ramaflex/tablas").Trim('/'); app.Map(rutaBase + "/{tabla}", async (HttpContext ctx) => { (int Estado, string Cuerpo) r; // EJEMPLO: un token fijo desde una variable de entorno. Se debe REEMPLAZAR por la autenticacion del // resto de su API, con una credencial de servicio para Ramaflex. var recibida = Encoding.UTF8.GetBytes(ctx.Request.Headers["Authorization"].ToString()); if (!CryptographicOperations.FixedTimeEquals(recibida, credencial)) { r = TablasRamaflex.Error(401, "NO_AUTENTICADO", "Credencial invalida."); } else { using var lector = new StreamReader(ctx.Request.Body, Encoding.UTF8); r = await tablas.AtenderAsync( ctx.Request.Method, (string)ctx.Request.RouteValues["tabla"]!, Parametros(ctx.Request.QueryString.Value), await lector.ReadToEndAsync()); } ctx.Response.StatusCode = r.Estado; ctx.Response.ContentType = "application/json; charset=utf-8"; await ctx.Response.WriteAsync(r.Cuerpo); }); app.Run("http://0.0.0.0:" + Variable("RAMAFLEX_PUERTO", "8080")); static string Variable(string nombre, string? porDefecto = null) => Environment.GetEnvironmentVariable(nombre) is { Length: > 0 } v ? v : porDefecto ?? throw new InvalidOperationException("Falta la variable de entorno " + nombre); // La query en orden, con repetidos y con los nombres tal cual. No se usa Request.Query porque agrupa los // nombres sin distinguir mayusculas ("orden" y la columna "ORDEN" se mezclarian). static List> Parametros(string? query) => (query ?? "").TrimStart('?') .Split('&', StringSplitOptions.RemoveEmptyEntries) .Select(par => par.Split('=', 2)) .Select(p => KeyValuePair.Create(Decodificar(p[0]), p.Length > 1 ? Decodificar(p[1]) : "")) .ToList(); static string Decodificar(string s) => Uri.UnescapeDataString(s.Replace('+', ' '));