Como Construir um Visualizador de PDF Blazor com IronPDF
O Problema Com a Geração de PDF Distribuído
Quando cada equipe que precisa de uma exportação de PDF constrói sua própria implementação, a base de código acaba com seis versões da mesma lógica de renderização, cada uma ligeiramente diferente, cada uma mantida por uma equipe diferente com prioridades diferentes. Um painel de BI tem seu próprio controlador de exportação. O painel de administração tem outro. O sistema financeiro tem um terceiro construído em torno de uma biblioteca diferente de três anos atrás. Nenhum deles produz documentos que pareçam ter vindo da mesma empresa.
O problema de acoplamento é tão caro quanto a duplicação. Quando um modelo de PDF precisa mudar, um aviso legal atualizado, um logo atualizado, uma coluna adicionada a um relatório padrão, essa mudança precisa ser implantada em cada aplicativo que incorpora sua própria lógica de renderização. Um modelo que deveria levar uma tarde para ser atualizado se torna um esforço de coordenação de equipes múltiplas e múltiplas iterações.
Alternativas tendem a introduzir problemas diferentes. Piping JSON através de uma ferramenta CLI funciona até que não funcione, e depurar um script shell que falha silenciosamente em produção é uma experiência miserável. APIs de geração de documentos externos resolvem o problema de isolamento, mas adicionam custos por chamada e uma viagem de ida e volta na rede para o que deveria ser uma capacidade interna. Equipes de operações solicitando relatórios ad-hoc ainda esperam um desenvolvedor escrever uma exportação personalizada, porque não há um endpoint genérico para chamar.
Cenários reais: um backend de BI que precisa oferecer exportação em PDF para qualquer gráfico ou tabela sem que cada dashboard tenha seu próprio renderizador, um sistema financeiro que chama um serviço interno para renderizar resumos de final de mês antes da distribuição, uma plataforma de QA gerando relatórios de teste em PDF a partir da saída JSON do pipeline CI, uma equipe de operações que deseja um único endpoint para transformar qualquer carga útil estruturada em um relatório formatado.
A Solução: Um Microserviço Dedicado de Renderização de HTML para PDF
IronPDF funciona como o motor de renderização dentro de um microserviço leve .NET que aceita JSON via HTTP POST, mapeia os dados para um HTML e CSS model template e retorna um PDF finalizado no corpo da resposta. Qualquer sistema interno como um backend de dashboard, um agendador, uma ferramenta CLI, ou outro microserviço, envia uma solicitação com o nome do modelo e uma carga útil JSON, e recebe de volta um binário PDF.
Alterações nos modelos são implantadas uma vez para o serviço de relatórios e têm efeito imediato para cada cliente, sem reimplantação coordenada entre equipes. Não há taxas SaaS por chamada, nenhuma lógica de renderização duplicada espalhada por aplicações e nenhuma incompatibilidade de versão de biblioteca entre equipes. O serviço funciona como uma aplicação .NET conteinerizada, um pacote NuGet, sem processos externos.
Como funciona na prática
1. Um Único Endpoint POST Aceita Nome do Modelo e Dados
O serviço expõe um endpoint: POST /api/reports/generate. O corpo da solicitação carrega um identificador de modelo e uma carga útil de dados JSON. O identificador de modelo mapeia para um arquivo HTML mantido pela equipe que possui o design do relatório, versionado junto com o serviço no controle de versão.
{
"template": "sumário-mensal",
"dados": {
"período": "Março 2025",
"receitaTotal": 482300.00,
"novasContas": 143,
"itensDeLinha": [...]
}
}
O serviço é a fonte única de verdade para cada formato PDF que a organização produz. Adicionar um novo tipo de relatório significa adicionar um novo template HTML e um novo identificador de modelo, sem necessidade de mudanças em qualquer sistema cliente.
Exemplo: Testando nosso Endpoint no Postman com Dados JSON

2. Template É Preenchido e Renderizado em C# PDF
Ao receber a solicitação, o serviço carrega o template HTML, desserializa a carga JSON para um modelo tipado ou dinâmico e preenche o template com os dados. Para relatórios estruturados com esquemas consistentes, uma etapa de desserialização tipada oferece segurança em tempo de compilação. Para cargas ad-hoc onde a schema varia por template, um JsonDocument ou binding dinâmico funciona com interpolação de strings ou uma biblioteca leve como o 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)
Documento PDF de saída

3. Sistemas Consumidores Chamam o Serviço via HTTP
Qualquer sistema interno que pode fazer um HTTP POST pode gerar um PDF sem incorporar qualquer lógica de renderização ou gerenciar qualquer dependência 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
Este código roda em qualquer sistema consumidor (aplicativo de console, serviço back-end ou microserviço), não dentro do próprio serviço de PDF. O sistema chamado recebe bytes de PDF brutos que pode transmitir para o navegador de um usuário, gravar no armazenamento de blob ou anexar a um e-mail de saída, o que quer que seu contexto exija. Ele não tem conhecimento de como o PDF foi produzido.
Saída: Relatório Gerado com aplicativo Console + sua API

4. O Serviço É Sem Estado e Observável
O serviço não retém dados entre solicitações. Cada renderização é independente, o que significa que o serviço escala horizontalmente atrás de um balanceador de carga ou em um implantação Kubernetes sem coordenação. Um pico na demanda de relatórios como rodadas no final do mês ou exportações em massa acionadas por uma tarefa agendada, é tratado pela adição de réplicas ao invés de mudanças na lógica do aplicativo.
Loggings estruturados capturam tempo de renderização, identificador de modelo, tamanho da carga e sucesso ou falha para cada solicitação. Métricas centralizadas mostram tendências de latência de renderização, taxas de erro por modelo e padrões de volume em um só lugar, ao invés de espalhadas pelos logs de cada aplicação cliente.
Benefícios no mundo real
Renderização centralizada. Um serviço é responsável pela geração de PDF em toda a organização. Atualizações de templates são implantadas uma vez e cada consumidor recebe a alteração sem tocar em sua própria base de código ou coordenar um lançamento.
Escalabilidade sem Estado. Nenhum estado compartilhado entre solicitações significa que o serviço escala horizontalmente para atender à demanda, adiciona réplicas, as remove quando a carga cai. Sem sessões fixas, nenhum cache distribuído necessário.
Saída consistente. Todo PDF produzido pelo serviço utiliza o mesmo motor de renderização, os mesmos templates e a mesma configuração. Não há divergência entre o que o sistema financeiro produz e o que os painéis exportam.
Integração rápida. Qualquer sistema que pode fazer um HTTP POST pode criar arquivos PDF. Não há SDK para incorporar no lado do consumidor, nenhuma versão de biblioteca para gerenciar e nenhuma importação para adicionar. O contrato é JSON na entrada, PDF na saída.
Observabilidade. Cada renderização é registrada e medida em um só lugar. Percentis de tempo de resposta, taxas de erro por modelo e volume diário são visíveis em um único painel ao invés de espalhados por várias aplicações.
Sem custos por chamada. O serviço roda em processo dentro de um container. Não há medição de API de terceiros e nenhum modelo de custos que escala com o volume de relatórios, gerar 100 ou 100.000 relatórios custa o mesmo em termos de infraestrutura.
Encerramento
Centralizar a geração de PDF em um microserviço dedicado troca um problema de manutenção distribuída por um simples: um serviço, um motor de renderização, um lugar para atualizar templates. Todo sistema interno que produz arquivos PDF se beneficia da mudança sem precisar fazer qualquer trabalho por si só.
O próprio serviço é uma aplicação mínima .NET, um endpoint, um carregador de template e uma chamada de renderização. IronPDF lida com todo o ciclo de vida da geração de PDF em C# em ironpdf.com, desde renderizar HTML modelos até salvar, transmitir e manipular documentos. Se você está pronto para construir e validar o serviço com suas próprias APIs internas, comece seu teste gratuito de 30 dias, é tempo suficiente para configurar o serviço, conectá-lo às suas fontes de dados e confirmar a saída antes de lançá-lo para sua equipe.




