如何在 C# 中將 Rotativa 遷移到 IronPDF
從 旋轉 遷移到 IronPDF 可以解決關鍵的安全漏洞,同時實現 PDF 生成工作流程的現代化。 本指南提供了一個完整的、逐步的遷移路徑,消除了已棄用的 wkhtmltopdf 依賴項,實現了對現代 CSS 和 JavaScript 的支持,並提供了超越 ASP.NET MVC 的跨平台相容性。
為什麼要從 旋轉 遷移到 IronPDF
了解輪蟲
Rotativa 長期以來一直是開發人員在 C# 中產生 PDF 的熱門選擇。 它利用wkhtmltopdf工具將 HTML 內容轉換為 PDF 格式。 旋轉 是一個專為 ASP.NET MVC 應用程式設計的開源程式庫。 然而,儘管 旋轉 吸引了大量用戶,但它對過時技術堆疊的依賴也帶來了一些挑戰,這些挑戰可能不會立即被每個開發者所察覺。
Rotativa 的核心功能是提供一種簡單的方法,將 PDF 生成整合到 ASP.NET MVC 專案中,並利用wkhtmltopdf的後端功能。
關鍵安全公告
Rotativa 封裝了 wkhtmltopdf,而 wkhtmltopdf 存在尚未修復的嚴重安全漏洞。
| 屬性 | 價值 |
|---|---|
| CVE ID | CVE-2022-35583 |
| 嚴重程度 | 評分:9.8/10 |
| 攻擊向量 | 網路 |
| 地位 | 永遠不會修復 |
| 做作的 | 所有 旋轉 版本 |
wkhtmltopdf 已於 2022 年 12 月正式停止維護。維護者明確表示他們不會修復安全漏洞。 所有使用 旋轉 的應用程式將永久暴露在外。
攻擊原理
<!-- Attacker submits this content via your MVC model -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" /><!-- Attacker submits this content via your MVC model -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />影響:
- 存取 AWS/Azure/GCP 雲端元資料端點 竊取內部 API 資料和憑證
- 連接埠掃描內部網路
- 洩漏敏感配置
技術危機
Rotativa wraps wkhtmltopdf,它使用:
- Qt WebKit 4.8(2012 年版)
- 不支援 Flexbox
- 不支援 CSS Grid
- JavaScript 執行失敗 不支援 ES6+
旋轉 與 IronPDF 對比
| 特徵 | 旋轉 | IronPDF |
|---|---|---|
| 專案相容性 | 僅限 ASP.NET MVC | 任何 .NET 專案類型(MVC、Razor Pages、Blazor 等) |
| 維護 | 棄 | 積極維護 |
| 安全 | 由於 wkhtmltopdf 依賴項而存在漏洞 (CVE-2022-35583) | 定期更新和安全性補丁 |
| HTML渲染 | 過時的 WebKit | 現代鉻 |
| CSS3 | 部分的 | 全力支持 |
| Flexbox/Grid | 不支援 | 全力支持 |
| JavaScript | 不可靠 | 完整的 ES6+ |
| Razor Pages | 不支援 | 全力支持 |
| 布雷澤 | 不支援 | 全力支持 |
| PDF 處理 | 無法使用 | 滿的 |
| 數位簽名 | 無法使用 | 滿的 |
| PDF/A 合規性 | 無法使用 | 滿的 |
| 異步/等待 | 僅同步 | 完全異步 |
| 開源 | 是的,MIT許可證 | 不,商業許可證 |
對於計劃在 2025 年和 2026 年採用 .NET 10 和 C# 14 的團隊,IronPDF 提供了 旋轉 無法提供的現代 Chromium 渲染和跨平台支援。
開始之前
先決條件
- .NET 環境: .NET Framework 4.6.2+ 或 .NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet 存取權限:能夠安裝 NuGet 套件
- IronPDF 許可證:請從ironpdf.com取得您的許可證密鑰。
NuGet 套件變更
# Remove Rotativa
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
# Install IronPDF
dotnet add package IronPdf# Remove Rotativa
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
# Install IronPDF
dotnet add package IronPdf移除 wkhtmltopdf 二進位文件
從項目中刪除以下文件:
wkhtmltopdf.exewkhtmltox.dll- 任何
Rotativa/資料夾
這些是 CVE-2022-35583 的來源。 IronPDF 不需要任何本機二進位。
許可證配置
// Add in Program.cs or Startup.cs
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";// Add in Program.cs or Startup.cs
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";完整 API 參考
命名空間變更
// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;核心類別映射
| 扶輪社 | IronPDF當量 | 筆記 |
|---|---|---|
ViewAsPdf | ChromePdfRenderer | 渲染 HTML |
ActionAsPdf | ChromePdfRenderer.RenderUrlAsPdf() | 渲染 URL |
UrlAsPdf | ChromePdfRenderer.RenderUrlAsPdf() | 渲染 URL |
Orientation枚舉 | PdfPaperOrientation枚舉 | 方向 |
Size枚舉 | PdfPaperSize枚舉 | 紙張尺寸 |
頁面佔位符轉換
| 輪蟲佔位符 | IronPDF佔位符 |
|---|---|
[page] | {page} |
[topage] | {total-pages} |
[date] | {date} |
[time] | {time} |
[title] | {html-title} |
[sitepage] | {url} |
程式碼遷移範例
範例 1:HTML 轉 PDF
之前(旋轉式):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
// 旋轉 requires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
// 旋轉 requires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}(IronPDF 之後):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}這個例子展示了架構上的根本差異。 旋轉 要求從 MVC 控制器操作返回ViewAsPdf結果,這可讓您與 ASP.NET MVC 框架綁定。 此模式僅適用於 MVC 請求管道,並且需要 Razor 視圖進行渲染。
IronPDF 可在任何地方運作:控制台應用程式、Web API、Blazor、Razor Pages 或任何 .NET 專案類型。 你使用 HTML 字串呼叫RenderHtmlAsPdf()並儲存結果。 無需MVC控制器,沒有視圖依賴。 請參閱HTML 轉 PDF 文件以取得完整範例。
範例 2:URL 轉 PDF
之前(旋轉式):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
// 旋轉 works within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
// 旋轉 works within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}(IronPDF 之後):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}Rotativa 的UrlAsPdf類別需要從 MVC 控制器傳回ActionResult 。 IronPDF 的RenderUrlAsPdf()方法可以從任何上下文中調用,並直接傳回PdfDocument物件。 URL渲染採用的是現代的Chromium內核,而非wkhtmltopdf有安全漏洞且過時的WebKit引擎。更多資訊請參閱我們的教學。
範例 3:帶頁碼的頁首和頁尾
之前(旋轉式):
// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [toPage]\""
};
}
}
}// NuGet: Install-Package Rotativa.Core
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [toPage]\""
};
}
}
}(IronPDF 之後):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}Rotativa 使用CustomSwitches將命令列參數傳遞給 wkhtmltopdf,包括帶有[page]和[toPage]等佔位符的頁首和頁尾配置。 這種基於字串的方法容易出錯,而且在編譯時很難驗證。
IronPDF 使用強型別的TextHeaderFooter對象,具有CenterText和DrawDividerLine等屬性。 佔位符語法從[page]變成{page} ,從[toPage]變成{total-pages} 。 類型化屬性提供智慧感知、編譯時檢查,且不會出現拼字錯誤。
僅 MVC 架構的問題
Rotativa 是為 ASP.NET MVC 5 及更早版本設計的:
// ❌ 旋轉 - Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integration// ❌ 旋轉 - Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integrationIronPDF 將視圖渲染與 PDF 生成分離,這實際上更加靈活——您可以渲染任何 HTML,而不僅僅是 MVC 視圖。
非同步模式遷移
Rotativa 阻斷了線; IronPDF 完全支持 async/await:
// ❌ 旋轉 - Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅ IronPDF - 完全異步 support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}// ❌ 旋轉 - Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅ IronPDF - 完全異步 support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}遷移後的新功能
遷移到 IronPDF 後,您將獲得 旋轉 無法提供的功能:
PDF合併
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");數位簽名
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);密碼保護
pdf.SecuritySettings.UserPassword = "secret";pdf.SecuritySettings.UserPassword = "secret";水印
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");PDF/A 歸檔合規性
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);現代 CSS 支持
// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!遷移清單
遷移前
- 識別程式碼庫中所有 旋轉 的使用情況
- 文檔 CustomSwitches 用於轉換為 RenderingOptions
- 注意頁首/頁尾佔位符的轉換語法(
[page]→{page}) - 從ironpdf.com取得 IronPDF 許可證金鑰
軟體包變更
- 刪除
Rotativa和Rotativa.AspNetCoreNuGet 套件 - 刪除 wkhtmltopdf 二進位(
wkhtmltopdf.exe、wkhtmltox.dll) 安裝IronPdfNuGet 套件
程式碼更改
- 更新命名空間導入(
using Rotativa;→using IronPdf;) - 將
ViewAsPdf替換為ChromePdfRenderer+RenderHtmlAsPdf() - 將
UrlAsPdf替換為RenderUrlAsPdf() - 將
CustomSwitches轉換為RenderingOptions屬性 - 更新佔位符語法(
[page]→{page},[topage]→{total-pages}) - 將
PageMargins替換為單獨的MarginTop/MarginBottom/MarginLeft/MarginRight - 在適當情況下改為非同步模式
- 在應用程式啟動時新增許可證初始化
移民後
- 驗證所有 PDF 產生功能是否正常
- 對比PDF輸出品質(Chromium渲染更精準)
- 驗證 CSS 渲染改進(Flexbox/Grid 現在可以正常運作)
- 測試 JavaScript 執行(現在使用 Chromium 核心已穩定運行)
- 驗證安全掃描通過(不再有 CVE-2022-35583 標誌)
- 更新 Docker 設定以移除 wkhtmltopdf 安裝






