Saltar al pie de página
USANDO IRONPDF

Cómo construir un visor de PDF Blazor con IronPDF

El problema con la generación distribuida de PDF

Página principal de IronPDF Cuando cada equipo que necesita una exportación PDF construye su propia implementación, la base de código termina con seis versiones de la misma lógica de renderizado, cada una ligeramente diferente, cada una mantenida por un equipo diferente con diferentes prioridades. Un panel de BI tiene su propio controlador de exportación. El panel de administración tiene otro. El sistema financiero tiene un tercero construido alrededor de una biblioteca diferente de hace tres años. Ninguno de ellos produce documentos que parecen provenir de la misma compañía.

El problema del acoplamiento es tan costoso como la duplicación. Cuando un modelo de PDF necesita cambiar, una renuncia legal actualizada, un logotipo renovado, una columna añadida a un informe estándar, ese cambio tiene que desplegarse en cada aplicación que embeba su propia lógica de renderizado. Una plantilla que debería tomar una tarde actualizarse se convierte en un esfuerzo de coordinación de múltiples equipos y múltiples sprints.

Las alternativas tienden a introducir diferentes problemas. Piping JSON a través de una herramienta de CLI funciona hasta que no lo hace, y depurar un script de shell que falla silenciosamente en producción es una experiencia miserable. Las APIs externas de generación de documentos resuelven el problema de aislamiento pero añaden costos por llamada y un recorrido de red para lo que debería ser una capacidad interna. Los equipos de operaciones que solicitan informes ad-hoc todavía esperan que un desarrollador escriba una exportación personalizada, porque no hay un punto de acceso de propósito general para llamar.

Escenarios reales: un backend de BI que necesita ofrecer exportación PDF para cualquier gráfico o tabla sin que cada panel tenga su propio renderizador, un sistema financiero que llama a un servicio interno para renderizar resúmenes de fin de mes antes de la distribución, una plataforma de control de calidad generando informes de ejecución de prueba PDF a partir de la salida JSON de la canalización de CI, un equipo de operaciones que quiere un único punto de acceso para convertir cualquier carga estructurada en un informe formateado.

La solución: un microservicio dedicado de renderizado de HTML a PDF

IronPDF actúa como el motor de renderizado dentro de un microservicio .NET liviano que acepta JSON a través de HTTP POST, mapea los datos en una plantilla HTML y CSS, y devuelve un PDF terminado en el cuerpo de la respuesta. Cualquier sistema interno como un backend de panel, un programador, una herramienta CLI, u otro microservicio, envía una solicitud con un nombre de plantilla y una carga JSON, y recibe de vuelta un binario PDF.

Los cambios en la plantilla se despliegan una sola vez en el servicio de informes y tienen efecto inmediato para cada consumidor, sin redesplegues coordinados a través de los equipos. No hay tarifas SaaS por llamada, no hay lógica de renderizado duplicada dispersa por las aplicaciones, y no hay desajustes de versiones de bibliotecas entre equipos. El servicio se ejecuta como una aplicación .NET en contenedor, un paquete NuGet, sin procesos externos.

Cómo funciona en la práctica

1. Un único punto final POST acepta nombre de plantilla y datos

El servicio expone un punto final: POST /api/reports/generate. El cuerpo de la solicitud lleva un identificador de plantilla y una carga de datos en formato JSON. El identificador de plantilla se mapea a un archivo HTML mantenido por el equipo que posee el diseño del informe, versionado junto con el servicio en el control de origen.


{
  "template": "resumen-mensual",
  "datos": {
    "periodo": "marzo 2025",
    "ingresosTotales": 482300.00,
    "nuevasCuentas": 143,
    "líneas": [...]
  }
}

El servicio es la única fuente de verdad para cada formato de PDF que produce la organización. Agregar un nuevo tipo de informe significa agregar una nueva plantilla HTML y un nuevo identificador de plantilla, sin necesidad de cambios en ningún sistema consumidor.

Ejemplo: Probando nuestro punto final en Postman con datos JSON

Internal Pdf Reporting Service 2 related to Ejemplo: Probando nuestro punto final en Postman con datos JSON

2. La plantilla se llena y se renderiza en PDF en C

Al recibir la solicitud, el servicio carga la plantilla HTML, deserializa la carga JSON en un modelo tipado o dinámico, y llena la plantilla con los datos. Para informes estructurados con esquemas consistentes, un paso de deserialización tipada brinda seguridad en tiempo de compilación. Para cargas ad-hoc donde el esquema varía según la plantilla, un JsonDocument o enlace dinámico funciona con interpolación de cadenas o una biblioteca ligera como Scriban.

using IronPdf;

using System.Text.Json;

app.MapPost("/api/reports/generate", async (HttpContext ctx) =>
{
    using var doc = await JsonDocument.ParseAsync(ctx.Request.Body);
    string templateId = doc.RootElement.GetProperty("template").GetString();
    var data = doc.RootElement.GetProperty("data");

    string templateHtml = await File.ReadAllTextAsync($"Templates/{templateId}.html");

    // Populate template with data values via string replacement or templating engine
    string html = templateHtml
        .Replace("{{period}}", data.GetProperty("period").GetString())
        .Replace("{{totalRevenue}}", data.GetProperty("totalRevenue").GetDecimal().ToString("C"))
        .Replace("{{newAccounts}}", data.GetProperty("newAccounts").GetInt32().ToString());

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;

    renderer.RenderingOptions.MarginTop = 20;

    renderer.RenderingOptions.MarginBottom = 20;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    ctx.Response.ContentType = "application/pdf";

    ctx.Response.Headers["Content-Disposition"] = $"attachment; filename=\"{templateId}-report.pdf\"";

    await ctx.Response.Body.WriteAsync(pdf.BinaryData);

});
using IronPdf;

using System.Text.Json;

app.MapPost("/api/reports/generate", async (HttpContext ctx) =>
{
    using var doc = await JsonDocument.ParseAsync(ctx.Request.Body);
    string templateId = doc.RootElement.GetProperty("template").GetString();
    var data = doc.RootElement.GetProperty("data");

    string templateHtml = await File.ReadAllTextAsync($"Templates/{templateId}.html");

    // Populate template with data values via string replacement or templating engine
    string html = templateHtml
        .Replace("{{period}}", data.GetProperty("period").GetString())
        .Replace("{{totalRevenue}}", data.GetProperty("totalRevenue").GetDecimal().ToString("C"))
        .Replace("{{newAccounts}}", data.GetProperty("newAccounts").GetInt32().ToString());

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;

    renderer.RenderingOptions.MarginTop = 20;

    renderer.RenderingOptions.MarginBottom = 20;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    ctx.Response.ContentType = "application/pdf";

    ctx.Response.Headers["Content-Disposition"] = $"attachment; filename=\"{templateId}-report.pdf\"";

    await ctx.Response.Body.WriteAsync(pdf.BinaryData);

});
Imports IronPdf
Imports System.Text.Json

app.MapPost("/api/reports/generate", Async Function(ctx As HttpContext) As Task
    Using doc = Await JsonDocument.ParseAsync(ctx.Request.Body)
        Dim templateId As String = doc.RootElement.GetProperty("template").GetString()
        Dim data = doc.RootElement.GetProperty("data")

        Dim templateHtml As String = Await File.ReadAllTextAsync($"Templates/{templateId}.html")

        ' Populate template with data values via string replacement or templating engine
        Dim html As String = templateHtml _
            .Replace("{{period}}", data.GetProperty("period").GetString()) _
            .Replace("{{totalRevenue}}", data.GetProperty("totalRevenue").GetDecimal().ToString("C")) _
            .Replace("{{newAccounts}}", data.GetProperty("newAccounts").GetInt32().ToString())

        Dim renderer As New ChromePdfRenderer()

        renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4

        renderer.RenderingOptions.MarginTop = 20

        renderer.RenderingOptions.MarginBottom = 20

        Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

        ctx.Response.ContentType = "application/pdf"

        ctx.Response.Headers("Content-Disposition") = $"attachment; filename=""{templateId}-report.pdf"""

        Await ctx.Response.Body.WriteAsync(pdf.BinaryData)
    End Using
End Function)
$vbLabelText   $csharpLabel

Documento PDF de salida

Ejemplo de PDF generado por IronPDF

3. Los sistemas consumidores llaman al servicio a través de HTTP

Cualquier sistema interno que pueda realizar un HTTP POST puede generar un PDF sin incrustar ninguna lógica de renderizado o gestionar ninguna dependencia de biblioteca:

using System.Net.Http;
using System.Text;
using System.Text.Json;

var payload = new
{
    template = "monthly-summary",
    data = new { period = "March 2025", totalRevenue = 482300.00, newAccounts = 143 }
};

using var client = new HttpClient { BaseAddress = new Uri("http://pdf-service.internal") };

var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");

using var response = await client.PostAsync("/api/reports/generate", content);

response.EnsureSuccessStatusCode();

byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();

// Stream to browser, save to storage, attach to email, etc.
using System.Net.Http;
using System.Text;
using System.Text.Json;

var payload = new
{
    template = "monthly-summary",
    data = new { period = "March 2025", totalRevenue = 482300.00, newAccounts = 143 }
};

using var client = new HttpClient { BaseAddress = new Uri("http://pdf-service.internal") };

var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");

using var response = await client.PostAsync("/api/reports/generate", content);

response.EnsureSuccessStatusCode();

byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();

// Stream to browser, save to storage, attach to email, etc.
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json

Dim payload = New With {
    .template = "monthly-summary",
    .data = New With {.period = "March 2025", .totalRevenue = 482300.0, .newAccounts = 143}
}

Using client As New HttpClient With {.BaseAddress = New Uri("http://pdf-service.internal")}
    Dim content As New StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")

    Using response As HttpResponseMessage = Await client.PostAsync("/api/reports/generate", content)
        response.EnsureSuccessStatusCode()

        Dim pdfBytes As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' Stream to browser, save to storage, attach to email, etc.
    End Using
End Using
$vbLabelText   $csharpLabel

Este código se ejecuta en cualquier sistema consumidor (aplicación de consola, servicio backend o microservicio), no dentro del propio servicio de PDF. El sistema que llama recibe bytes PDF sin procesar que puede transmitir al navegador de un usuario, escribir en almacenamiento blob, o adjuntar a un correo electrónico saliente, lo que sea que requiera su contexto. No tiene conocimiento de cómo se produjo el PDF.

Salida: Informe generado con la aplicación de consola + su API

Documento PDF generado

4. El servicio es sin estado y observable

El servicio no mantiene datos entre solicitudes. Cada renderizado es independiente, lo que significa que el servicio escala horizontalmente detrás de un equilibrador de carga o en una implementación de Kubernetes sin coordinación. Un aumento en la demanda de informes como en ejecuciones de fin de mes o exportaciones masivas desencadenadas por un trabajo programado, se maneja agregando réplicas en lugar de cambiar la lógica de la aplicación.

El registro estructurado captura el tiempo de renderizado, el identificador de plantilla, el tamaño de la carga, y el éxito o el fracaso de cada solicitud. Los métricos centralizados exhiben tendencias de latencia de renderizado, tasas de error por plantilla, y patrones de volumen en un solo lugar, en lugar de dispersos por los registros de cada aplicación consumidora.

Beneficios reales

Renderizado centralizado. Un servicio posee la generación de PDF en toda la organización. Las actualizaciones de plantillas se despliegan una sola vez y cada consumidor recibe el cambio sin tocar su propia base de código o coordinar un lanzamiento.

Escalabilidad sin estado. No hay estado compartido entre solicitudes, lo que significa que el servicio escala horizontalmente para satisfacer la demanda, agregando réplicas, eliminándolas cuando la carga disminuye. No hay sesiones pegajosas, no se requiere caché distribuido.

Salida consistente. Cada PDF producido por el servicio utiliza el mismo motor de renderizado, las mismas plantillas, y la misma configuración. No hay divergencia entre lo que produce el sistema financiero y lo que exporta el dashboard.

Integración rápida. Cualquier sistema que pueda realizar un HTTP POST puede crear archivos PDF. No hay SDK para incrustar en el lado del consumidor, no hay versión de biblioteca para gestionar, y no hay importación para añadir. El contrato es JSON de entrada, PDF de salida.

Observabilidad. Cada renderizado se registra y mide en un solo lugar. Los percentiles de tiempo de respuesta, las tasas de error por plantilla, y el volumen diario son visibles en un único panel de control en lugar de dispersos a través de múltiples aplicaciones.

No hay costos por llamada. El servicio se ejecuta en proceso dentro de un contenedor. No hay medición de API de terceros y no hay un modelo de costos que escale con el volumen de informes, generar 100 o 100,000 informes cuesta lo mismo en términos de infraestructura.

Cierre

Centralizar la generación de PDF en un microservicio dedicado cambia un problema de mantenimiento distribuido por uno simple: un servicio, un motor de renderizado, un lugar para actualizar plantillas. Cada sistema interno que produce archivos PDF se beneficia del cambio sin hacer ningún trabajo ellos mismos.

El servicio en sí es una aplicación .NET mínima, un punto final, un cargador de plantillas, y una llamada de renderizado. IronPDF maneja el ciclo de vida completo de la generación de PDF en C# en ironpdf.com, desde renderizar HTML plantillas hasta guardar, transmitir y manipular documentos. Si está listo para construir y validar el servicio contra sus propias APIs internas, comience su prueba gratuita de 30 días, es tiempo suficiente para activar el servicio, conectarlo a sus fuentes de datos y confirmar la salida antes de implementarlo en su equipo.

Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien ...

Leer más

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame