Jak zbudować przeglądarkę plików PDF dla Blazora przy użyciu IronPDF
Problem z rozproszoną generacją PDF
Gdy każdy zespół, który potrzebuje eksportu PDF, buduje własną implementację, baza kodu kończy z sześcioma wersjami tej samej logiki renderowania, każda nieznacznie inna, każda utrzymywana przez inny zespół z różnymi priorytetami. Dashboard BI ma własny kontroler eksportu. Panel administracyjny ma inny. System finansowy ma trzeci, zbudowany wokół innej biblioteki z trzy lata temu. Żaden z nich nie produkuje dokumentów, które wyglądają, jakby pochodziły z tej samej firmy.
Problem związku jest równie kosztowny jak duplikacja. Gdy szablon PDF musi się zmienić, zaktualizować ostrzeżenie prawne, odświeżyć logo, dodać kolumnę do standardowego raportu, ta zmiana musi być wdrożona w każdej aplikacji, która wbudowuje własną logikę renderowania. Szablon, który powinien zająć popołudnie do aktualizacji, staje się projektem skoordynowanym wielu zespołów i wielu sprintów.
Alternatywy mają tendencję do wprowadzania różnych problemów. Prowadzenie JSON przez narzędzie CLI działa, dopóki nie działa, a debugowanie skryptu powłoki, który nie działa cicho w produkcji, to koszmarne doświadczenie. Zewnętrzne APzły dokument generowania APQ rozwiązują problem izolacji, ale dodają koszty zapytań i podróży sieci wynikające z tego, co powinno być wewnętrzną możliwością. Zespoły operacyjne żądające raportów ad-hoc wciąż czekają na programistę, aby napisać niestandardowy eksport, ponieważ nie ma ogólnego punktu końcowego do wywołania.
Rzeczywiste scenariusze: Backend BI, który musi oferować eksport PDF dla dowolnego wykresu lub tabeli bez konieczności każdorazowego działania z poziomu tablicy kontrolnej, system finansowy, który wywołuje wewnętrzną usługę do renderowania podsumowań na koniec miesiąca przed dystrybucją, platforma QA generująca raporty z testów PDF z jsonowych wyników CI, zespół operacyjny, który chce mieć jeden punkt końcowy do przekształcenia dowolnego strukturalnego ładunku w sformatowany raport.
Rozwiązanie: Dedykowana mikroserwis HTML do PDF
IronPDFd działa jako silnik renderowania w lekkim mikroserwisie .NET, który akceptuje JSON przez HTTP POST, mapuje dane do HTML i szablon CSS, a w odpowiedzi zwraca gotowy PDF. Każdy wewnętrzny system, taki jak backend dashboardu, harmonogram, narzędzie CLI lub inny mikroserwis, wysyła zapytanie z nazwą szablonu i ładunkiem JSON, otrzymując z powrotem binarny PDF.
Zmiany szablonu wdrażane są raz dla usługi raportowania i są natychmiast efektywne dla każdego klienta, bez skoordynowanego wdrożenia zespołów. Nie ma opłat Saas za zapytania, nie ma zduplikowanej logiki renderowania rozrzuconej po aplikacjach, nie ma niezgodności wersji bibliotek między zespołami. Usługa działa jako kontenerowa aplikacja .NET, jeden pakiet NuGet, bez procesów zewnętrznych.
Jak to działa w praktyce
1. Jeden punkt końcowy POST akceptuje nazwę szablonu i dane
Usługa udostępnia jeden punkt końcowy: POST /api/reports/generate. Ciało żądania zawiera identyfikator szablonu i ładunek danych JSON. Identyfikator szablonu mapuje się na plik HTML utrzymywany przez zespół, do którego należy projekt raportu, wersjonowany wraz z usługą w systemie kontroli wersji.
{
"szablon": "podsumowanie-miesieczne",
"data": {
"period": "Marzec 2025",
"totalRevenue": 482300.00,
"newAccounts": 143,
"lineItems": [...]
}
}
Usługa jest jedynym źródłem prawdy dla każdego formatu PDF, który produkuje organizacja. Dodanie nowego typu raportu oznacza dodanie nowego szablonu HTML i nowego identyfikatora szablonu, nie są wymagane zmiany w żadnym systemie konsumenckim.
Przykład: Testowanie naszego punktu końcowego w Postman z danymi JSON

2. Szablon jest wypełniany i renderowany w PDF C
Po otrzymaniu żądania, usługa ładuje szablon HTML, deserializuje ładunek JSON do typowanego lub dynamicznego modelu i wypełnia szablon danymi. Dla raportów strukturalnych z konsekwentnymi schematami, krok deserializacji typów zapewnia bezpieczeństwo w czasie kompilacji. Dla ładunków ad-hoc, gdzie schemat różni się w zależności od szablonu, dokument JSON lub wiązanie dynamiczne działa z interpolacją łańcuchową lub lekką biblioteką, taką jak 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)
Wynikowy dokument PDF

3. Systemy konsumenckie wywołują usługę przez HTTP
Kazdy wewnetrzny system, ktory moze wykonac HTTP POST, moze generowac plik PDF bez wbudowywania logiki renderowania lub zarzadzania zaleznoscia biblioteki:
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
Ten kod dziala w kazdym systemie korzystajacym (aplikacja konsolowa, usluga backendowa lub mikrousługa), a nie wewnatrz samej uslugi PDF. System wywolujacy otrzymuje surowe bajty PDF, ktore moze przeslac do przegladarki uzytkownika, zapisac w magazynie blobow lub dolaczyc do wychodzacego e-maila, cokolwiek wymaga jego kontekst. Nie ma wiedzy na temat tego, jak PDF zostal wyprodukowany.
Wyjscie: Raport wygenerowany przy uzyciu aplikacji konsolowej + Twojego API

4. Usługa jest bezstanowa i obserwowalna
Usługą nie przechowuje danych pomiedzy zadaniami. Kazde renderowanie jest niezalezne, co oznacza, ze usluga skaluje sie poziomo za load balancerem lub w wdrozeniu Kubernetes bez koordynacji. Wzrost zapotrzebowania na raporty, takie jak koniec miesiaca lub masowe eksporty uruchamiane przez zaplanowane zadanie, jest obslugiwany poprzez dodanie replik, a nie zmiane logiki aplikacji.
Strukturalne logowanie rejestruje czas renderowania, identyfikator szablonu, wielkosc danych i sukces lub niepowodzenie dla kazdego zadania. Centralne metryki pokazuja trendy opoznien renderowania, bledow wedlug szablonow oraz wzorce wolumenu w jednym miejscu, zamiast rozsianych po logach kazdej aplikacji korzystajacej.
Korzyści w praktyce
Centralne renderowanie. Jedna usluga odpowiada za generowanie PDF w calej organizacji. Aktualizacje szablonow wdrazane sa raz i kazdy uzytkownik otrzymuje zmiane bez dotykania własnej bazy kodu lub koordynacji wydania.
Bezstanowa skalowalność. Brak wspólnego stanu pomiędzy ządaniami oznacza, że serwis skaluje się poziomo, żeby sprostać zapotrzebowaniu, dodaje repliki, usuwa je, gdy obciążenie spada. Brak sesji przypisanych, brak rozproszonych pamieci podrecznych wymaganych.
Spójne wyjście. Kazdy PDF produkowany przez serwis uzywa tego samego silnika renderowania, tych samych szablonow i tej samej konfiguracji. Nie ma zadnych roznic pomiedzy tym, co produkuje system finansowy, a tym, co eksportuje pulpit nawigacyjny.
Szybka integracja. Kazdy system, ktory moze wykonac HTTP POST, moze tworzyc pliki PDF. Nie ma zadnego SDK do osadzania po stronie użytkownika, zadnej wersji biblioteki do zarzadzania i zadnego importu do dodania. Kontrakt stanowi JSON na wejsciu, PDF na wyjsciu.
Obserwowalność. Kazde renderowanie jest logowane i mierzone w jednym miejscu. Percentyle czasu odpowiedzi, wspolczynniki bledow wedlug szablonow i dzienny wolumen sa widoczne w jednym pulpicie nawigacyjnym, a nie rozproszone pomiedzy wieloma aplikacjami.
Brak kosztow na wezwanie. Usługa działa w procesie wewnątrz kontenera. Nie ma zadnego mierzenia API zewnetrznych i zadnego modelu kosztow skalujacego sie wraz z wolumenem raportow, generowanie 100 czy 100 000 raportow kosztuje tyle samo w kontekscie infrastruktury.
Zakończenie
Centralizacja generowania PDF w dedykowanej mikrousłudze zamienia problem rozproszonej konserwacji na prosty: jedna usługa, jeden silnik renderowania, jedno miejsce do aktualizacji szablonów. Kazdy wewnetrzny system, ktory produkuje pliki PDF, korzysta z tej zmiany bez zadnej pracy po swojej stronie.
Sama usługa to minimalna aplikacja .NET, endpoint, loader szablonów i wywołanie render. IronPDF obsługuje cały cykl życia generowania PDF w C# na ironpdf.com, od renderowania szablonów HTML po zapisywanie, przesyłanie strumieniowe i manipulowanie dokumentami. Jesli jestes gotowy na zbudowanie i walidacje uslugi z wlasnymi API wewnetrznymi, rozpocznij 30-dniowy bezplatny okres probny, to wystarczy czasu na zbudowanie uslugi, połączenie jej z źródłami danych i potwierdzenie wyjścia przed wprowadzeniem jej do zespołu.




