如何用 C# 從 Rotativa 轉移到 IronPDF
從Rotativa轉移到 IronPDF:完整的 C# 遷移指南。
從Rotativa轉移到IronPDF可解決重要的安全漏洞,同時使您的 PDF 生成工作流程現代化。 本指南提供了一個完整的、循序漸進的遷移路徑,消除了廢棄的 wkhtmltopdf 相依性,啟用對現代 CSS 和 JavaScript 的支援,並提供超越 ASP.NET MVC 的跨平台相容性。
為何要從Rotativa轉移到 IronPDF?
瞭解 Rotativa
Rotativa 一直以來都是開發人員在 C# 中產生 PDF 的熱門選擇。 它利用 wkhtmltopdf 工具將 HTML 內容轉換成 PDF 格式。Rotativa是專為 ASP.NET MVC 應用程式設計的開放源碼函式庫。 然而,Rotativa 雖然吸引了相當多的受眾,但其依賴過時的技術堆疊所帶來的挑戰,可能並非每個開發人員都能立即察覺。
Rotativa 的核心是利用 wkhtmltopdf 的後端功能,提供簡單的方式將 PDF 生成整合到 ASP.NET MVC 專案中。
重要安全諮詢
Rotativa包裝了wkhtmltopdf,而wkhtmltopdf具有嚴重的未配對安全漏洞。
| 屬性 | 價值 |
|---|---|
| CVE ID | CVE-2022-35583 |
| 嚴重性 | 關鍵 (9.8/10) |
| 攻擊向量 | 網路 |
| 狀態 | 永不打補丁 |
| 受影響 | 所有Rotativa版本 |
wkhtmltopdf 已於 2022 年 12 月正式廢棄。維護者明確表示他們不會修復安全漏洞。 每個使用Rotativa的應用程式都會永久曝光。
攻擊如何運作
<!-- 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 包裝了 wkhtmltopdf,它使用:
- Qt WebKit 4.8 (自 2012 年起)
- 不支援 Flexbox
- 不支援 CSS 網格
- 破碎的 JavaScript 執行
- 不支援 ES6+
Rotativa與IronPDF的比較
| 特點 | Rotativa | IronPDF |
|---|---|---|
| 專案相容性 | 僅限 ASP.NET MVC | 任何 .NET 專案類型 (MVC、Razor Pages、Blazor 等) |
| 維護 | 棄 | 積極維護 |
| 安全性 | 由於 wkhtmltopdf 相依性而產生漏洞 (CVE-2022-35583) | 定期更新和安全修補程式 |
| HTML 渲染 | 過時的 WebKit | 現代 Chromium |
| CSS3。 | 部分的 | 全面支援 |
| Flexbox/網格 | 不支援 | 全面支援 |
| JavaScript。 | 不可靠 | 完整的 ES6+ |
| Razor 頁面 | 不支援 | 全面支援 |
| Blazor | 不支援 | 全面支援 |
| PDF 操作 | 無法提供 | 全文 |
| 數位簽名 | 無法提供 | 全文 |
| PDF/A合規性 | 無法提供 | 全文 |
| Async/等待 | 僅同步 | 完全同步 |
| 開放原始碼 | 是,MIT 授權 | 無,商業授權 |
對於計劃在 2025 年和 2026 年之前採用 .NET 10 和 C# 14 的團隊,IronPDF 可提供Rotativa無法提供的現代 Chromium 渲染和跨平台支援。
開始之前
先決條件
1..NET 環境:.NET Framework 4.6.2+ 或 .NET Core 3.1+ / .NET 5/6/7/8/9+ 2.NuGet存取:安裝 NuGet 套件的能力 3.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.exe</編碼
- <編碼>wkhtmltox.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";IRON VB CONVERTER ERROR developers@ironsoftware.com完整的 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;IRON VB CONVERTER ERROR developers@ironsoftware.com核心類對應
| Rotativa 類別 | IronPdf 同等級產品 | 筆記 |
|---|---|---|
| <編碼>ViewAsPdf</編碼 | <代碼>ChromePdfRenderer</代碼 | 渲染 HTML |
| <程式碼>ActionAsPdf</程式碼 | <代碼>ChromePdfRenderer.RenderUrlAsPdf()</代碼 | 渲染 URL |
| <編碼>UrlAsPdf</編碼 | <代碼>ChromePdfRenderer.RenderUrlAsPdf()</代碼 | 渲染 URL |
方向枚舉 | PdfPaperOrientation 枚舉 | 導向 |
Size 枚舉 | PdfPaperSize 枚舉 | 紙張大小 |
頁面占位符轉換
| Rotativa 佔位符 | IronPdf 占位符 |
|---|---|
[page] | {page} |
[topage] | {總頁數} |
[日期] | <編碼>{日期}</編碼 |
[時間] | {time} |
[標題] | <編碼>{html-title}</編碼 |
| <編碼>[網站頁面]</編碼 | <編碼>{url}</編碼 |
程式碼遷移範例
範例 1:HTML 到 PDF 的轉換
之前 (Rotativa):
// 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>";
//Rotativarequires 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>";
//Rotativarequires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.comAfter (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!");
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.com本範例展示了基本的架構差異。Rotativa要求從 MVC 控制器動作返回<編碼>ViewAsPdf</編碼結果,將您與 ASP.NET MVC Framework 連結在一起。 該模式僅在 MVC 請求管道中運作,並需要 Razor 視圖來呈現。
IronPDF 可在任何地方工作:控制台應用程式、Web API、Blazor、Razor Pages 或任何 .NET 專案類型。 您使用 HTML 字串呼叫 RenderHtmlAsPdf() 並儲存結果。 不需要 MVC 控制器,無視圖依賴性。 請參閱 HTML to PDF 文件,以瞭解全面的範例。
範例 2:URL 到 PDF 的轉換
之前 (Rotativa):
// 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()
{
//Rotativaworks 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()
{
//Rotativaworks 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
};
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.comAfter (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!");
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.comRotativa 的<編碼>UrlAsPdf</編碼類需要從 MVC 控制器傳回 ActionResult 。IronPDF的 RenderUrlAsPdf() 方法可從任何上下文中呼叫,並直接返回 PdfDocument 物件。 URL 渲染使用現代的 Chromium,而非 wkhtmltopdf 脆弱且過時的 WebKit 引擎。請參閱我們的 教學,瞭解更多資訊。
範例 3:帶有頁碼的頁首和頁尾
之前 (Rotativa):
// 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]\""
};
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.comAfter (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!");
}
}
}IRON VB CONVERTER ERROR developers@ironsoftware.comRotativa 使用 CustomSwitches 將命令列參數傳給 wkhtmltopdf,包括使用佔位符如[page]和 [toPage] 的頁首和頁尾設定。 這種以字串為基礎的方法容易出錯,且很難在編譯時驗證。
IronPDF 使用強式類型的 TextHeaderFooter 物件,其屬性包括 CenterText 和 DrawDividerLine 等。 佔位符語法從[page]變更為{page}以及從 [toPage] 變更為{總頁數}。 類型化的屬性提供 IntelliSense、編譯時檢查,而且沒有錯字的風險。
僅 MVC 架構的問題
Rotativa 專為 ASP.NET MVC 5 及更早版本所設計:
// ❌Rotativa- 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// ❌Rotativa- 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 integrationIRON VB CONVERTER ERROR developers@ironsoftware.comIronPDF 將視圖渲染與 PDF 產生分離,這其實更具彈性-您可以渲染任何 HTML,而不僅僅是 MVC 視圖。
同步模式轉換
Rotativa 阻斷線程; IronPdf 支援完整的 async/await:
// ❌Rotativa- 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
}// ❌Rotativa- 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
}IRON VB CONVERTER ERROR developers@ironsoftware.com遷移後的新功能
轉移到IronPDF之後,您將獲得Rotativa無法提供的功能:
PDF 合併
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");IRON VB CONVERTER ERROR developers@ironsoftware.com數位簽名
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);IRON VB CONVERTER ERROR developers@ironsoftware.com密碼保護
pdf.SecuritySettings.UserPassword = "secret";pdf.SecuritySettings.UserPassword = "secret";IRON VB CONVERTER ERROR developers@ironsoftware.com水印。
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");IRON VB CONVERTER ERROR developers@ironsoftware.comPDF/A 檔案合規性
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);IRON VB CONVERTER ERROR developers@ironsoftware.com現代 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!IRON VB CONVERTER ERROR developers@ironsoftware.com遷移清單
預遷移
- [ ] 識別程式碼庫中所有Rotativa的用法
- [文件 CustomSwitches 用於轉換為 RenderingOptions
- [ ] 轉換時請注意頁首/頁尾佔位符語法 (
[page]→{page}) - [ ] 從 ironpdf.com 獲得IronPDF授權金鑰
套件變更
- [ ] 移除
Rotativa和Rotativa.AspNetCoreNuGet 套件 - [ ] 刪除 wkhtmltopdf 的二進位檔 (
wkhtmltopdf.exe,wkhtmltox.dll) - [ ] 安裝
IronPdfNuGet 套件
程式碼變更
- [ ] 更新命名空間匯入 (
using Rotativa;→using IronPdf;) - [ ] 使用<代碼>ChromePdfRenderer</代碼+
RenderHtmlAsPdf()取代<編碼>ViewAsPdf</編碼。 - [ ] 將<編碼>UrlAsPdf</編碼替換為
RenderUrlAsPdf() - [ ] 將
CustomSwitches轉換為RenderingOptions屬性 - [ ] 更新占位符語法(
[page]→{page},[topage]→{total-pages}) - [ ] 將
PageMargins替換為個別的MarginTop/MarginBottom/MarginLeft/MarginRight。 - [ ] 在適當的地方改用 async 模式
- [ ] 在應用程式啟動時加入授權初始化
後遷移
- [ ] 確認所有 PDF 生成工作正常進行
- [ ] 比較 PDF 輸出品質 (Chromium 的渲染更精確)
- [驗證 CSS 渲染的改進(Flexbox/Grid 現在可以工作了)
- [ ] 測試 JavaScript 執行 (現在使用 Chromium 的可靠度)
- [驗證安全掃描通過 (不再有CVE-2022-35583標誌)
- [ ] 更新 Docker 配置以移除 wkhtmltopdf 安裝
結論
雖然Rotativa歷來為 ASP.NET MVC 應用程式中的 PDF 生成提供了直接的解決方案,但其對過時技術堆疊的依賴以及在維護方面的捨棄構成了重大的挑戰。CVE-2022-35583的重要安全漏洞永遠無法修補,會永久暴露每個Rotativa應用程式。
本次轉換的主要變更如下 1.安全性:關鍵 SSRF 漏洞 (CVE-2022-35583) → 受保護 2.架構:僅 MVC → 任何 .NET 專案類型 3.渲染引擎:過時的 WebKit (2012) → 現代的 Chromium 4.CSS 支援:部分 (無 Flexbox/Grid) → 完整 CSS3 支援 5.JavaScript:不可靠 → 完全 ES6+ 支援 6.樣式:ViewAsPdf 返回類型 →<代碼>ChromePdfRenderer</代碼+ RenderHtmlAsPdf() 7.URL Rendering:UrlAsPdf → RenderUrlAsPdf() 8.標題/註腳:CustomSwitches 字串 → 類型化的 TextHeaderFooter 物件 9.Placeholder:[page],[topage]→ {page}, {total-pages} 10.Async 支援:僅同步 → 完全 async/await 11.維護:拋棄 → 每週更新
對於在多樣化的 .NET 應用程式中生成多功能且安全的 PDF 感興趣的開發人員,會發現IronPDFfor .NET 是更令人信服的選擇。IronPDF 不僅能確保與各種應用程式的相容性,還能提供持續的支援與更新,這在今日快速演進的科技環境中至關重要。
探索完整的 IronPDF說明文件、教學和API參考,加速您的Rotativa轉移。






