Comment construire un visualiseur de PDF Blazor avec IronPDF
Le problème avec la génération de PDF distribuée
Lorsque chaque équipe qui a besoin d'une exportation PDF construit sa propre implémentation, la base de code se retrouve avec six versions de la même logique de rendu, chacune légèrement différente, chacune maintenue par une équipe différente avec des priorités différentes. Un tableau de bord BI a son propre contrôleur d'exportation. Le panneau d'administration en a un autre. Le système financier en a un troisième construit autour d'une bibliothèque différente de il y a trois ans. Aucune d'elles ne produit de documents qui semblent provenir de la même entreprise.
Le problème de couplage est aussi coûteux que la duplication. Lorsque le modèle de PDF doit changer, un avis légal mis à jour, un logo rafraîchi, une colonne ajoutée à un rapport standard, ce changement doit être déployé dans chaque application qui intègre sa propre logique de rendu. Un modèle qui devrait prendre un après-midi à mettre à jour devient un effort de coordination multi-équipe, multi-sprint.
Les alternatives ont tendance à introduire différents problèmes. Acheminer du JSON par un outil CLI fonctionne jusqu'à ce que cela ne fonctionne plus, et déboguer un script shell qui échoue silencieusement en production est une expérience misérable. Les APIs de génération de documents externes résolvent le problème d'isolation mais ajoutent des coûts par appel et un aller-retour réseau pour ce qui devrait être une capacité interne. Les équipes Ops demandant des rapports ad-hoc attendent toujours qu'un développeur écrive une exportation personnalisée, car il n'y a pas de point de terminaison généraliste à appeler.
Scénarios réels : un backend BI qui doit offrir une exportation PDF pour tout graphique ou tableau sans que chaque tableau de bord possède son propre moteur de rendu, un système financier qui appelle un service interne pour rendre les résumés de fin de mois avant distribution, une plateforme de QA générant des rapports de résultats de test PDF à partir de la sortie JSON du pipeline CI, une équipe Ops qui veut un point de terminaison unique pour transformer n'importe quelle charge utile structurée en rapport formaté.
La solution : un microservice dédié au rendu HTML en PDF
IronPDF agit comme le moteur de rendu à l'intérieur d'un microservice léger .NET qui accepte le JSON via HTTP POST, mappant les données sur un modèle HTML et CSS, et renvoie un PDF fini dans le corps de la réponse. Tout système interne tel qu'un backend de tableau de bord, un planificateur, un outil CLI, ou un autre microservice, envoie une requête avec un nom de modèle et une charge JSON, et reçoit un binaire PDF.
Les modifications du modèle se déploient une fois au service de reporting et prennent effet pour chaque consommateur immédiatement, sans redéploiements coordonnés entre équipes. Il n'y a pas de frais SaaS par appel, pas de logique de rendu dupliquée éparpillée à travers les applications, et pas de décalage de version de bibliothèque entre équipes. Le service fonctionne en tant qu'application .NET conteneurisée, un package NuGet, pas de processus externes.
Comment ça marche en pratique
1. Un seul point de terminaison POST accepte le nom de modèle et les données
Le service expose un point de terminaison : POST /api/reports/generate. Le corps de la requête contient un identifiant de modèle et une charge JSON. L'identifiant du modèle correspond à un fichier HTML maintenu par l'équipe qui possède le design du rapport, versionné avec le service dans le contrôle de source.
{
"template": "résumé-mensuel",
"data": {
"period": "Mars 2025",
"totalRevenue": 482300,00
"newAccounts": 143
"lineItems": [...]
}
}
Le service est la source unique de référence pour chaque format de PDF que l'organisation produit. Ajouter un nouveau type de rapport signifie ajouter un nouveau modèle HTML et un nouvel identifiant de modèle, aucune modification requise dans aucun système consommateur.
Exemple : tester notre point de terminaison dans Postman avec des données JSON

2. Le modèle est peuplé et rendu en C# PDF
En recevant la demande, le service charge le modèle HTML, désérialise la charge JSON dans un modèle typé ou dynamique, et remplit le modèle avec les données. Pour les rapports structurés avec des schémas cohérents, une étape de désérialisation typée offre une sécurité au moment de la compilation. Pour les charges ponctuelles où le schéma varie selon le modèle, un JsonDocument ou une liaison dynamique fonctionne avec l'interpolation de chaînes ou une bibliothèque légère comme 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)
Document PDF de sortie

3. Les systèmes consommateurs appellent le service via HTTP
Tout système interne pouvant faire un HTTP POST peut générer un PDF sans intégrer de logique de rendu ni gérer une dépendance de bibliothèque :
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
Ce code s'exécute dans n'importe quel système consommateur (application console, service backend, ou microservice), pas à l'intérieur du service PDF lui-même. Le système appelant reçoit des octets bruts de PDF qu'il peut diffuser vers le navigateur d'un utilisateur, écrire dans le stockage d'objets, ou joindre à un email sortant, tout ce que son contexte nécessite. Il n'a pas connaissance de comment le PDF a été produit.
Sortie : Rapport généré avec l'application Console + votre API

4. Le service est sans état et observable
Le service ne retient aucune donnée entre les requêtes. Chaque rendu est indépendant, ce qui signifie que le service évolue horizontalement derrière un équilibre de charge ou dans un déploiement Kubernetes sans coordination. Un pic de demande de rapport comme les exécutions de fin de mois ou les exportations en masse déclenchées par une tâche planifiée, est géré en ajoutant des réplicas plutôt qu'en modifiant la logique de l'application.
La journalisation structurée capte le temps de rendu, l'identifiant du modèle, la taille de la charge, et le succès ou l'échec de chaque demande. Des métriques centralisées mettent en évidence des tendances de latence de rendu, des taux d'erreur par modèle, et des schémas de volume en un seul endroit, plutôt que dispersés à travers les journaux de chaque application consommatrice.
Avis Réels
Rendu centralisé. Un service possède la génération de PDF à travers l'organisation. Les mises à jour de modèles se déploient une fois et chaque consommateur obtient le changement sans toucher à leur propre base de code ou coordonner une version.
Évolutivité sans état. Aucun état partagé entre les requêtes signifie que le service évolue horizontalement pour répondre à la demande, ajoute des réplicas, les enlève lorsque la charge diminue. Pas de sessions collantes, pas de cache distribué requis.
Sortie cohérente. Chaque PDF produit par le service utilise le même moteur de rendu, les mêmes modèles, et la même configuration. Il n'y a pas de divergence entre ce que produit le système financier et ce que le tableau de bord exporte.
Intégration rapide. Tout système pouvant faire un HTTP POST peut créer des fichiers PDF. Il n'y a pas de SDK à intégrer côté consommateur, pas de version de bibliothèque à gérer, et pas d'importation à ajouter. Le contrat est du JSON en entrée, PDF en sortie.
Observabilité. Chaque rendu est journalisé et mesuré en un seul endroit. Les percentiles de temps de réponse, les taux d'erreur par modèle, et le volume quotidien sont visibles dans un tableau de bord unique plutôt que dispersés à travers plusieurs applications.
Pas de coûts par appel. Le service s'exécute en processus à l'intérieur d'un conteneur. Il n'y a pas de mesure API tierce et pas de modèle de coût qui évolue avec le volume de rapport, générer 100 ou 100 000 rapports coûte le même prix en termes d'infrastructure.
Conclusion
Centraliser la génération de PDF dans un microservice dédié échange un problème de maintenance distribué pour un simple : un service, un moteur de rendu, un endroit pour mettre à jour les modèles. Chaque système interne qui produit des fichiers PDF bénéficie du changement sans faire aucun travail eux-mêmes.
Le service lui-même est une application .NET minimaliste, un point de terminaison, un chargeur de modèle, et un appel de rendu. IronPDF gère le cycle de vie complet de la génération de PDF en C# sur ironpdf.com, de rendu HTML à la sauvegarde, au streaming, et à la manipulation de documents. Si vous êtes prêt à construire et valider le service par rapport à vos propres APIs internes, commencez votre essai gratuit de 30 jours, c'est assez de temps pour mettre en place le service, le connecter à vos sources de données, et confirmer la sortie avant de le déployer à votre équipe.




