Saltar al pie de página
GUíAS DE MIGRACIóN

Cómo migrar de Rotativa a IronPDF en C#

La migración de RotativaaIronPDFresuelve vulnerabilidades de seguridad críticas a la vez que moderniza el flujo de trabajo de generación de PDF. Esta guía proporciona una ruta de migración completa, paso a paso, que elimina la dependencia wkhtmltopdf abandonada, permite la compatibilidad con CSS y JavaScript modernos y proporciona compatibilidad entre plataformas más allá de ASP.NET MVC.

Por qué migrar de Rotativaa IronPDF

Entendiendo Rotativa

Rotativa ha sido durante mucho tiempo una opción popular entre los desarrolladores para generar archivos PDF en C#. Aprovecha la herramienta wkhtmltopdf para convertir contenido HTML a formato PDF. Rotativaes una biblioteca de código abierto diseñada específicamente para aplicaciones ASP.NET MVC. Sin embargo, aunque ha atraído a un público significativo, la dependencia de Rotativade una pila tecnológica obsoleta presenta retos que pueden no ser evidentes de inmediato para todos los desarrolladores.

En esencia, Rotativaproporciona una forma sencilla de integrar la generación de PDF en proyectos ASP.NET MVC, aprovechando wkhtmltopdf para sus funcionalidades de backend.

Aviso de seguridad crítico

Rotativa envuelve wkhtmltopdf, que tiene VULNERABILIDADES CRÍTICAS DE SEGURIDAD NO COMPLETADAS.

Atributo Valor
CVE ID CVE-2022-35583
Severity CRÍTICO (9,8/10)
Vector de ataque Red
Estado NUNCA SERÁ PARCHEADO
Afectados TODAS las versiones de Rotativa

wkhtmltopdf fue oficialmente abandonado en diciembre de 2022. Los mantenedores declararon explícitamente que NO corregirán las vulnerabilidades de seguridad. Todas las aplicaciones que utilizan Rotativaestán permanentemente expuestas.

Cómo funciona el ataque


<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />

<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />
HTML

Impacto:

  • Acceso a los puntos finales de metadatos de la nube de AWS/Azure/GCP
  • Robo de datos y credenciales de API internas
  • Escaneado de puertos de redes internas
  • Exfiltrar configuración sensible

La crisis tecnológica

Rotativa envuelve wkhtmltopdf, que utiliza:

  • Qt WebKit 4.8 (desde 2012)
  • No es compatible con Flexbox
  • No es compatible con CSS Grid
  • Ejecución rota de JavaScript
  • No es compatible con ES6+

Comparación entre Rotativae IronPDF

Característica Rotativa IronPDF
Compatibilidad de proyectos Sólo ASP.NET MVC Cualquier tipo de proyecto .NET (MVC, Razor Pages, Blazor, etc.)
Mantenimiento Abandonado Mantenimiento activo
Seguridad Vulnerable debido a las dependencias de wkhtmltopdf (CVE-2022-35583) Actualizaciones periódicas y parches de seguridad
Representación HTML WebKit obsoleto Chromium moderno
CSS3 Parcial Soporte completo
Flexbox/Grid No soportado Soporte completo
JavaScript Poco fiable ES6+ completo
Páginas de Razor No soportado Soporte completo
Blazor No soportado Soporte completo
Manipulación de PDF No disponible Completo
Firmas digitales No disponible Completo
Cumplimiento de PDF/A No disponible Completo
Async/Await Sólo sincrónico Async completo
Código abierto Sí, licencia MIT No, Licencia comercial

Para los equipos que planifican la adopción de .NET 10 y C# 14 hasta 2025 y 2026,IronPDFproporciona un moderno renderizado en Chromium y una compatibilidad multiplataforma que Rotativano puede ofrecer.


Antes de empezar

Prerrequisitos

  1. Entorno .NET: .NET Framework 4.6.2+ o .NET Core 3.1+ / .NET 5/6/7/8/9+
  2. Acceso a NuGet: Capacidad para instalar paquetes NuGet
  3. Licencia de IronPDF: Obtenga su clave de licencia en ironpdf.com

Cambios en el paquete 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

Quitar los binarios de wkhtmltopdf

Elimine estos archivos de su proyecto:

  • <código>wkhtmltopdf.exe- <código>wkhtmltox.dll</code>- Cualquier carpetaRotativa/`

Estas son el origen de CVE-2022-35583.IronPDFno necesita binarios nativos.

Configuración de licencias

// 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";
$vbLabelText   $csharpLabel

Referencia completa de la API

Cambios en el espacio de nombres

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

Asignaciones de clases principales

Clase Rotativa Equivalente de IronPDF Notas
<código>VerComoPdf` <código>ChromePdfRenderer Renderizar HTML
<código>ActionAsPdf <código>ChromePdfRenderer.RenderUrlAsPdf() Renderizar URL
<código>UrlAsPdf <código>ChromePdfRenderer.RenderUrlAsPdf() Renderizar URL
Orientación enum PdfPaperOrientation enum Orientación
Tamaño enum PdfPaperSize enum Tamaño del papel

Conversión de marcadores de posición

Marcador de posición de Rotativa Marcador de posición IronPDF
<código>[página]|{page}`
[topage] {total de páginas}
<código>[fecha]|{fecha}`
<código>[tiempo]|{time}`
<código>[título]|{html-title}`
[sitepage] {url}

Ejemplos de migración de código

Ejemplo 1: Conversión de HTML a PDF

Antes (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
            };
        }
    }
}
$vbLabelText   $csharpLabel

Después (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!");
        }
    }
}
$vbLabelText   $csharpLabel

Este ejemplo demuestra la diferencia arquitectónica fundamental. Rotativarequiere devolver un resultado <código>VerComoPdf`de una acción de controlador MVC, lo que le vincula al marco ASP.NET MVC. El patrón sólo funciona dentro del canal de solicitudes MVC y requiere una vista Razor para su representación.

IronPDF funciona en cualquier lugar: aplicaciones de consola, API web, Blazor, Razor Pages o cualquier tipo de proyecto .NET. Se llama a RenderHtmlAsPdf() con una cadena HTML y se guarda el resultado. No requiere controlador MVC, ni dependencia de vistas. Consulte la documentación HTML a PDF para ver ejemplos completos.

Ejemplo 2: Conversión de URL a PDF

Antes (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
            };
        }
    }
}
$vbLabelText   $csharpLabel

Después (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!");
        }
    }
}
$vbLabelText   $csharpLabel

La clase <código>UrlAsPdfde Rotativarequiere devolver unActionResultdesde un controlador MVC. El métodoRenderUrlAsPdf()deIronPDFpuede invocarse desde cualquier contexto y devuelve directamente un objetoPdfDocument`. El renderizado de URL utiliza el moderno Chromium en lugar del vulnerable y anticuado motor WebKit de wkhtmltopdf. Obtenga más información en nuestros tutoriales.

Ejemplo 3: Encabezados y pies de página con números de página

Antes (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]\""
            };
        }
    }
}
$vbLabelText   $csharpLabel

Después (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!");
        }
    }
}
$vbLabelText   $csharpLabel

Rotativa utiliza CustomSwitches para pasar argumentos de línea de comandos a wkhtmltopdf, incluida la configuración de encabezados y pies de página con marcadores de posición como <código>[página]y[toPage]`. Este enfoque basado en cadenas es propenso a errores y difícil de validar en tiempo de compilación.

IronPDF utiliza objetos TextHeaderFooter con propiedades como CenterText y DrawDividerLine. La sintaxis del marcador de posición cambia de <código>[página]a{page}y de[toPage]a{total-pages}`. Las propiedades tipificadas ofrecen IntelliSense, comprobación en tiempo de compilación y ausencia de riesgo de errores tipográficos.


El problema de la arquitectura MVC

Rotativa se ha diseñado para ASP.NET MVC 5 y versiones anteriores:

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

IronPDF separa la renderización de vistas de la generación de PDF, lo que en realidad es más flexible: se puede renderizar cualquier HTML, no sólo vistas MVC.


Migración de patrones asíncronos

Rotativa bloquea el hilo;IronPDFes totalmente compatible con 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- Async completosupport
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- Async completosupport
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
}
$vbLabelText   $csharpLabel

Nuevas capacidades tras la migración

Tras migrar a IronPDF, obtendrá funciones que Rotativano puede ofrecer:

Fusión de PDF

var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");
$vbLabelText   $csharpLabel

Firmas digitales

var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);
$vbLabelText   $csharpLabel

Protección con contraseña

pdf.SecuritySettings.UserPassword = "secret";
pdf.SecuritySettings.UserPassword = "secret";
$vbLabelText   $csharpLabel

Marcas de agua

pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");
$vbLabelText   $csharpLabel

Cumplimiento de la normativa sobre archivos PDF/A

pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
$vbLabelText   $csharpLabel

Soporte CSS moderno

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

Lista de comprobación de la migración

Pre-Migración

  • Identificar todo el uso de Rotativaen el código base
  • Documentar CustomSwitches utilizados para la conversión a RenderingOptions
  • Nota: Sintaxis de marcador de posición de encabezado/pie de página para conversión ( <código>[página]{page}`)
  • Obtenga la clave de licencia deIronPDFen ironpdf.com

Cambios en el paquete

  • Eliminar los paquetes NuGet Rotativa y Rotativa.AspNetCore
  • Eliminar los binarios de wkhtmltopdf ( wkhtmltopdf.exe , wkhtmltox.dll )
  • Instalar el paquete NuGet IronPdf

Cambios de código

  • Actualizar las importaciones de espacios de nombres ( using Rotativa;using IronPdf; )
  • Reemplazar <código>VerComoPdfcon <código>ChromePdfRenderer</code>+RenderHtmlAsPdf()`
  • Reemplazar <código>UrlAsPdfconRenderUrlAsPdf()`
  • Convertir CustomSwitches en propiedades RenderingOptions
  • Actualizar la sintaxis del marcador de posición ( <código>[página]{page},[topage]{total de páginas}`)
  • Reemplace PageMargins con MarginTop / MarginBottom / MarginLeft / MarginRight individuales
  • Cambiar al patrón asíncrono cuando sea apropiado
  • Agregar inicialización de licencia al iniciar la aplicación

Posmigración

  • Verificar que toda la generación de PDF funcione correctamente
  • Comparar la calidad de salida de PDF (Chromium renderiza con más precisión)
  • Verificar mejoras en la representación CSS (Flexbox/Grid ahora funcionan)
  • Prueba la ejecución de JavaScript (ahora confiable con Chromium)
  • Verificar los pases de escaneo de seguridad (no más indicadores CVE-2022-35583)
  • Actualizar las configuraciones de Docker para eliminar la instalación de wkhtmltopdf

Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien ...

Leer más