IronPDFでBlazor PDFビューアを構築する方法
分散PDF生成の問題
PDFエクスポートを必要とするチームごとに独自の実装があり、そのコードベースにはわずかに異なる6つのレンダリングロジックのバージョンが各チームの異なる優先順位によって維持されています。 BIダッシュボードには独自のエクスポートコントローラーがあります。 管理パネルには別のものがあります。 3年前に異なるライブラリ周りに構築された3つ目のものが財務システムにあります。 どれも同じ会社から来たようには見えないドキュメントを生成しません。
結合の問題は重複と同じくらいコストがかかります。 PDFテンプレートが変更されるか、法的な免責事項が更新されるか、ロゴが刷新されるか、標準レポートに列が追加されるか、その変更は埋め込まれている全てのアプリケーションに展開されなければなりません。 午後に更新すべきテンプレートが、マルチチーム、マルチスプリントの調整作業になってしまいます。
代替案は異なる問題を引き起こす傾向があります。 JSONをCLIツールを経由してパイプ処理するのは、うまくいかなくなるまで機能し、サイレントに失敗するシェルスクリプトのデバッグは惨めな体験です。 外部ドキュメント生成APIは孤立の問題を解決しますが、1回の呼び出しごとのコストや内部機能であるべきものにネットワーク往復を追加します。 Opsチームがアドホックなレポートを要求するときには、一般的なエンドポイントがないために開発者がカスタムエクスポートを作成するのを待っています。
実際のシナリオ: BIバックエンドがダッシュボードに独自のレンダラーを持たずに任意のチャートやテーブルのPDFエクスポートを提供する必要がある場合、 財務システムは内部サービスを呼び出して、配布前の月末サマリーをレンダリング、QAプラットフォームPDFテスト CIパイプラインJSON出力からのテスト実行レポートを生成、そしてOpsチームが任意の構造化ペイロードをフォーマット化レポートに変換する単一のエンドポイントを望んでいます。
ソリューション: HTMLからPDFへのレンダリング専用マイクロサービス
IronPDFは、JSONをHTTP POSTで受け入れる軽量.NETマイクロサービス内でレンダリングエンジンとして機能し、データをHTMLおよびCSSテンプレートにマッピングし、応答本文に完成したPDFを返します。 ダッシュボードバックエンドやスケジューラー、CLIツール、あるいは他のマイクロサービスなどの内部システムが、テンプレート名とJSONペイロードを持つ要求を送信すると、PDFバイナリを返します。
テンプレートの変更はレポートサービスに一度デプロイされ、すべてのコンシューマーに即座に効果を発揮し、チーム間でのデプロイの調整は不要です。 1回ごとのSaaS料金やアプリ全体に散在する重複したレンダリングロジック、チーム間のライブラリバージョンの不一致はありません。 サービスは、コンテナ化された.NETアプリケーションとして実行され、一つのNuGetパッケージです。外部プロセスは不要です。
実際の使用方法
1. 単一のPOSTエンドポイントがテンプレート名とデータを受け入れる
サービスは1つのエンドポイントを公開しています: POST /api/reports/generate。 リクエストボディには、テンプレート識別子とJSONデータペイロードが含まれます。 テンプレート識別子は、そのテンプレートデザインを所有するチームによって維持されるHTMLファイルにマッピングされ、ソース管理でサービスとともにバージョン管理されます。
{
"template": "monthly-summary",
"data": {
"period": "2025年3月",
"totalRevenue": 482300.00,
"newAccounts": 143,
"lineItems": [...]
}
}
サービスは、組織が生成するすべてのPDFフォーマットの唯一の真実のソースです。 新しいレポートタイプの追加は、新しいHTMLテンプレートとテンプレート識別子を追加することを意味し、消費するシステムに変更は不要です。
例: PostmanでJSONデータを用いてエンドポイントをテスト

2. C# PDFでテンプレートが埋め込まれ、レンダリングされる
リクエストを受信すると、サービスはHTMLテンプレートをロードし、JSONペイロードを型付きまたは動的モデルにデシリアライズし、データでテンプレートを埋め込みます。 一貫したスキーマを持つ構造化レポートには、型付きデシリアライズステップにより、コンパイル時の安全性がもたらされます。 テンプレートによってスキーマが異なるアドホックペイロードには、JsonDocumentまたは動的バインディングが文字列補完か、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)
出力PDFドキュメント

3. コンシューマーシステムはHTTPでサービスに呼び出す
HTTP POSTを実行できる内部システムは、レンダリングロジックを埋め込まず、ライブラリ依存関係を管理せずにPDFを生成できます。
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
このコードはコンシューマーシステム内(コンソールアプリ、バックエンドサービス、またはマイクロサービス)で実行されます。PDFサービスの中では実行されません。 呼び出しシステムは、ユーザーのブラウザにストリームする、生のPDFバイトを受信し、ブロブストレージに書き込む、または外部メールに添付する、などその状況が求めるものに対応します。 それはPDFがどのように生成されたのかを知りません。
出力: コンソールアプリ + APIで生成されたレポート

4. サービスはステートレスで観察可能である
サービスはリクエスト間にデータを保持しません。 各レンダリングは独立しているため、サービスは負荷分散装置背後またはKubernetesデプロイメント内で協調なしで水平スケールします。 月末ランやスケジュールされたジョブによって引き起こされる大量輸出などのレポート需要の急増は、アプリケーションロジックを変更せずにレプリカを追加することで処理されます。
構造化されたログは、各リクエストのレンダリング時間、テンプレート識別子、ペイロードサイズ、成功または失敗をキャプチャします。集中メトリックスは、レンダレイトレンド、テンプレート別エラーレート、ボリュームパターンを一箇所に表示します。これは、すべての消費アプリケーションのログに散在しているのではなくデータセンター内で見られます。
実世界の利点
中央集権のレンダリング。 組織全体でのPDF生成は1つのサービスが担う。 テンプレートの更新は1度デプロイされ、全ての消費者は自身のコードベースを触れずに、リリースの調整なしに更新を受け取ります。
ステートレスなスケーラビリティ。 リクエスト間に共有状況がないので、需要に応じてサービスが水平にスケールし、レプリカを追加できます。負荷が減少したときに削除できます。 セッションのスティッキー化はなく、分散キャッシュは必要ありません。
一貫した出力。 サービスが生成するすべてのPDFは同じレンダリングエンジン、同じテンプレート、および同じ設定を用いています。 財務システムが生成するものとダッシュボードがエクスポートするものとの間に差異はありません。
迅速な統合。 HTTP POSTを実行できる任意のシステムでPDFファイルを作成できます。 消費者側でエンベッドするSDKはなく、管理するライブラリバージョンはなく、追加するインポートもありません。 契約はJSON入力、PDF出力です。
観察可能性。 各レンダリングは1カ所でログかつ測定されます。 応答時間のパーセンタイル、テンプレートに基づくエラーレート、および日々のボリュームは、複数のアプリケーションに分散するのではなく1つのダッシュボードで見ることができます。
1回の呼び出しコストなし。 サービスはコンテナ内でインプロセスで実行されます。 サードパーティAPIによるメータリングはありませんし、レポートボリュームに応じて費用が増大するコストモデルもありません。100または100,000のレポートの生成がインフラ面で同じコストです。
結び
専用のマイクロサービスにPDF生成を集中することにより、分散メンテナンス問題を1つの簡単な問題に変換します。1つのサービス、1つのレンダリングエンジン、テンプレートを更新する1つの場所です。 すべての内部システムは、いかなる作業も必要とせずに変化から恩恵を受けるPDFファイルを生成しています。
サービス自体は、最小限 for .NETアプリケーション、エンドポイント、テンプレートローダー、レンダリングコールです。 ironpdf.com では、IronPDFがPDF生成の完全なライフサイクルをC#内で管理し、HTMLをレンダリングするテンプレートからドキュメントを保存したりストリームしたり操作したりします。 ご自身の内部APIに対してのサービス構築と検証を開始する準備ができたら、無料の30日間トライアルを開始してください。サービスを立ち上げ、データソースに接続し、チームに配信する前に出力を確認するのに十分な時間です。




