跳過到頁腳內容
遷移指南

如何用 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 IDCVE-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" />
HTML

影響:

  • 存取 AWS/Azure/GCP 雲端元資料端點
  • 竊取內部 API 資料和憑證
  • 連接埠掃描內部網路
  • 滲出敏感組態

技術危機

Rotativa 包裝了 wkhtmltopdf,它使用:

  • Qt WebKit 4.8 (自 2012 年起)
  • 不支援 Flexbox
  • 不支援 CSS 網格
  • 破碎的 JavaScript 執行
  • 不支援 ES6+

Rotativa與IronPDF的比較

特點RotativaIronPDF
專案相容性僅限 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
SHELL

移除 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
$vbLabelText   $csharpLabel

完整的 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
$vbLabelText   $csharpLabel

核心類對應

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.com
$vbLabelText   $csharpLabel

After (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
$vbLabelText   $csharpLabel

本範例展示了基本的架構差異。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.com
$vbLabelText   $csharpLabel

After (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.com
$vbLabelText   $csharpLabel

Rotativa 的<編碼>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.com
$vbLabelText   $csharpLabel

After (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.com
$vbLabelText   $csharpLabel

Rotativa 使用 CustomSwitches 將命令列參數傳給 wkhtmltopdf,包括使用佔位符如[page][toPage] 的頁首和頁尾設定。 這種以字串為基礎的方法容易出錯,且很難在編譯時驗證。

IronPDF 使用強式類型的 TextHeaderFooter 物件,其屬性包括 CenterTextDrawDividerLine 等。 佔位符語法從[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 integration
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

IronPDF 將視圖渲染與 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
$vbLabelText   $csharpLabel

遷移後的新功能

轉移到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
$vbLabelText   $csharpLabel

數位簽名

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
$vbLabelText   $csharpLabel

密碼保護

pdf.SecuritySettings.UserPassword = "secret";
pdf.SecuritySettings.UserPassword = "secret";
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

水印。

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.com
$vbLabelText   $csharpLabel

PDF/A 檔案合規性

pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

現代 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
$vbLabelText   $csharpLabel

遷移清單

預遷移

  • [ ] 識別程式碼庫中所有Rotativa的用法
  • [文件 CustomSwitches 用於轉換為 RenderingOptions
  • [ ] 轉換時請注意頁首/頁尾佔位符語法 ([page]{page})
  • [ ] 從 ironpdf.com 獲得IronPDF授權金鑰

套件變更

  • [ ] 移除 RotativaRotativa.AspNetCore NuGet 套件
  • [ ] 刪除 wkhtmltopdf 的二進位檔 (wkhtmltopdf.exe, wkhtmltox.dll)
  • [ ] 安裝 IronPdf NuGet 套件

程式碼變更

  • [ ] 更新命名空間匯入 (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 RenderingUrlAsPdfRenderUrlAsPdf() 8.標題/註腳CustomSwitches 字串 → 類型化的 TextHeaderFooter 物件 9.Placeholder[page],[topage]{page}, {total-pages} 10.Async 支援:僅同步 → 完全 async/await 11.維護:拋棄 → 每週更新

對於在多樣化的 .NET 應用程式中生成多功能且安全的 PDF 感興趣的開發人員,會發現IronPDFfor .NET 是更令人信服的選擇。IronPDF 不僅能確保與各種應用程式的相容性,還能提供持續的支援與更新,這在今日快速演進的科技環境中至關重要。

探索完整的 IronPDF說明文件教學API參考,加速您的Rotativa轉移。

Curtis Chau
技術作家

Curtis Chau 擁有卡爾頓大學計算機科學學士學位,專注於前端開發,擅長於 Node.js、TypeScript、JavaScript 和 React。Curtis 熱衷於創建直觀且美觀的用戶界面,喜歡使用現代框架並打造結構良好、視覺吸引人的手冊。

除了開發之外,Curtis 對物聯網 (IoT) 有著濃厚的興趣,探索將硬體和軟體結合的創新方式。在閒暇時間,他喜愛遊戲並構建 Discord 機器人,結合科技與創意的樂趣。