跳至页脚内容
迁移指南

如何用 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 的核心是提供一种简单的方法,将 PDF 生成集成到 ASP.NET MVC 项目中,并利用 wkhtmltopdf 实现其后台功能。

重要安全咨询

Rotativa封装了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 进行了包装,后者使用的是.NET:

  • 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合规性不可用满的
同步/等待仅同步完全异步
开放源代码是,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 枚举定位
<代码>大小</代码>枚举PdfPaperSize 枚举纸张大小

页面占位符转换

Rotativa 占位符IronPdf 占位符
<代码>[页面]</代码{page}
<代码>[topage]</代码<代码>{总页数}</代码
<代码>[日期]</代码<代码>{日期}</代码
<代码>[时间]</代码<代码>{时间}</代码
<代码>[标题]</代码<代码>{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 转 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,包括带有占位符(如<代码>[页面]</代码和 [toPage] )的页眉和页脚配置。 这种基于字符串的方法容易出错,而且很难在编译时进行验证。

IronPDF 使用强类型的 TextHeaderFooter 对象,该对象具有 CenterTextDrawDividerLine 等属性。 占位符语法从<代码>[页面]</代码变为{page},从 [toPage] 变为<代码>{总页数}</代码。 类型化属性提供了智能提示、编译时检查功能,而且不会出现错别字。


仅 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 支持完全异步/等待:

// ❌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的用法
  • [用于转换为 RenderingOptions 的文件 CustomSwitches
  • [ ] 注意转换时的页眉/页脚占位符语法([page]{page})
  • [ ] 从 ironpdf.com 获取IronPDF许可证密钥

软件包变更

  • [ ] 删除 RotativaRotativa.AspNetCore NuGet 软件包
  • [ ] 删除 wkhtmltopdf 二进制文件 (wkhtmltopdf.exe, wkhtmltox.dll)
  • [ ] 安装 IronPdf NuGet 软件包

代码更改

  • [ ] 更新命名空间导入(使用 Rotativa;使用 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/网格)→完全 CSS3 支持 5.JavaScript:不可靠 → 完全支持 ES6+ 6.模式:<代码>ViewAsPdf</代码>返回类型 → <代码>ChromePdfRenderer</代码> + <代码>RenderHtmlAsPdf()</代码 7.URL 渲染:<代码>UrlAsPdf</代码> → <代码>RenderUrlAsPdf()</代码 8.页眉/页脚CustomSwitches 字符串 → 类型 TextHeaderFooter 对象 9.占位符[page],<代码>[topage]</代码→ {page}, {total-pages} 10.异步支持:仅同步 → 完全异步/等待 11.维护:放弃 → 每周更新

对在各种.NET 应用程序中多功能、安全地生成 PDF 感兴趣的开发人员会发现IronPDFfor .NET 是一个更有说服力的选择。IronPDF 不仅能确保与各种应用程序的兼容性,还能提供持续的支持和更新,这在当今快速发展的技术环境中至关重要。

探索完整的IronPDF文档教程API参考,加速您的Rotativa迁移。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。