You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:将Razor页面转换为PDF的可行替代方案

Razor页面转PDF替代方案

我尝试用IronPDF和iTextSharp将Razor页面转为PDF时持续报错,排查错误时还导致应用无响应,现在卡在这个任务上,求其他实现方案和代码。以下是待转换的Razor页面及后台代码:

待转换的Razor页面(Print.cshtml)

@page
@model TestApp01.Pages.Clients.PrintModel
@{}

<br>
<h2>Edit Client</h2>

@if (Model.errorMessage.Length > 0)
{
    <div class='alert alert-warning alert-dismissible fade show' role='alert'>
        <strong>@Model.errorMessage</strong>
        <button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button>
    </div>
}

<form method="POST">
    <input type="hidden" name="id" value="@Model.clientInfo.id"/>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">Policy Number:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="policy_number" value="@Model.clientInfo.policy_number" readonly/>
        </div>
    </div>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">First Name:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="first_name" value="@Model.clientInfo.first_name" readonly/>
        </div>
    </div>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">Last Name:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="last_name" value="@Model.clientInfo.last_name" readonly/>
        </div>
    </div>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">Email:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="email" value="@Model.clientInfo.email" readonly/>
        </div>
    </div>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">Phone:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="phone" value="@Model.clientInfo.phone" readonly/>
        </div>
    </div>
    <div class="row mb-1">
        <label class="col-sm-3 col-form-label">Address:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" name="address" value="@Model.clientInfo.address" readonly/>
        </div>
    </div>
    
     <div class="row mb-3">
        <div class="offset-sm-3 col-sm-3 d-grid">
            <button type="submit" class="btn btn-primary" value="Submit">Submit</button>
        </div>
        <div class="col-sm-3 d-grid">
            <a class="btn btn-outline-primary" href="/Clients/Index" role="button">Cancel</a>
        </div>
        </div>
</form>

对应的后台代码(Print.cshtml.cs)

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Data.SqlClient;

namespace TestApp01.Pages.Clients
{
    public class PrintModel : PageModel
    {
        public ClientInfo clientInfo = new ClientInfo();
        public string errorMessage = "";
        public string successMessage = "";
        
        public void OnGet()
        {
            string id = Request.Query["id"];
            try
            {
                string connectionString = "myDB";
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    connection.Open();
                    string sql = "SELECT * FROM clients WHERE id=@id";
                    using (SqlCommand command = new SqlCommand(sql, connection))
                    {
                        command.Parameters.AddWithValue("@id", id);
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                clientInfo.id = "" + reader.GetInt32(0);
                                clientInfo.policy_number = reader.GetString(1);
                                clientInfo.first_name = reader.GetString(2);
                                clientInfo.last_name = reader.GetString(3);
                                clientInfo.email = reader.GetString(4);
                                clientInfo.phone = reader.GetString(5);
                                clientInfo.address = reader.GetString(6);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }
        }

        public void OnPost()
        {
            clientInfo.id = Request.Form["id"];
            clientInfo.policy_number = Request.Form["policy_number"];
            clientInfo.first_name = Request.Form["first_name"];
            clientInfo.last_name = Request.Form["last_name"];
            clientInfo.email = Request.Form["email"];
            clientInfo.phone = Request.Form["phone"];
            clientInfo.address = Request.Form["address"];

            if (clientInfo.policy_number.Length == 0 || clientInfo.first_name.Length == 0 ||
                clientInfo.last_name.Length == 0 || clientInfo.email.Length == 0 ||
                clientInfo.phone.Length == 0 || clientInfo.address.Length == 0)
            {
                errorMessage = "All the fields are required";
                return;
            }
           
        }
    }

    // 补充原代码缺失的ClientInfo类
    public class ClientInfo
    {
        public string id { get; set; }
        public string policy_number { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string email { get; set; }
        public string phone { get; set; }
        public string address { get; set; }
    }
}

替代方案1:使用DinkToPdf(基于wkhtmltopdf)

这是稳定性较高的方案,依赖wkhtmltopdf的HTML渲染能力,支持完整的CSS和JavaScript。

实现步骤:

  1. 安装NuGet包:
Install-Package DinkToPdf
# 根据服务器系统安装对应运行时包,例如Windows系统:
Install-Package DinkToPdf.Runtime.Windows
  1. 在Program.cs中注册PDF转换器服务:
using DinkToPdf;
using DinkToPdf.Contracts;

var builder = WebApplication.CreateBuilder(args);

// 注册同步PDF转换器
builder.Services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));

// 其他服务注册
builder.Services.AddRazorPages();

var app = builder.Build();

// 中间件配置...
app.Run();
  1. 修改PrintModel,添加生成PDF的处理方法:
using DinkToPdf.Contracts;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using System.IO;
using System.Threading.Tasks;

namespace TestApp01.Pages.Clients
{
    public class PrintModel : PageModel
    {
        private readonly IConverter _pdfConverter;
        private readonly ICompositeViewEngine _viewEngine;

        // 注入转换器和视图引擎
        public PrintModel(IConverter pdfConverter, ICompositeViewEngine viewEngine)
        {
            _pdfConverter = pdfConverter;
            _viewEngine = viewEngine;
        }

        // 原有OnGet、OnPost方法保持不变...

        // 新增生成PDF的POST处理方法
        public async Task<IActionResult> OnPostGeneratePdf()
        {
            // 渲染当前Razor页面为HTML字符串
            var htmlContent = await RenderViewToStringAsync();

            // 配置PDF文档参数
            var pdfConfig = new HtmlToPdfDocument
            {
                GlobalSettings = {
                    ColorMode = ColorMode.Color,
                    Orientation = Orientation.Portrait,
                    PaperSize = PaperKind.A4,
                    Margins = new MarginSettings { Top = 15, Bottom = 15, Left = 10, Right = 10 }
                },
                Objects = {
                    new ObjectSettings {
                        HtmlContent = htmlContent,
                        WebSettings = { 
                            DefaultEncoding = "utf-8",
                            // 设置基础URL,确保外部CSS/图片能加载
                            BaseUrl = $"{Request.Scheme}://{Request.Host}"
                        },
                        HeaderSettings = {
                            FontSize = 10,
                            Center = "Client Details",
                            Line = true
                        },
                        FooterSettings = {
                            FontSize = 8,
                            Right = $"Page [page] of [toPage] | Generated on {DateTime.Now:yyyy-MM-dd}"
                        }
                    }
                }
            };

            // 生成PDF字节数组
            var pdfBytes = _pdfConverter.Convert(pdfConfig);

            // 返回PDF文件供下载
            return File(pdfBytes, "application/pdf", $"Client_Details_{clientInfo.id}.pdf");
        }

        // 辅助方法:异步渲染视图为HTML字符串
        private async Task<string> RenderViewToStringAsync()
        {
            var viewResult = _viewEngine.FindView(ControllerContext, "Print", false);
            if (!viewResult.Success)
            {
                throw new InvalidOperationException($"无法找到视图:Print");
            }

            using var sw = new StringWriter();
            var viewContext = new ViewContext(
                ControllerContext,
                viewResult.View,
                ViewData,
                TempData,
                sw,
                new HtmlHelperOptions()
            );

            await viewResult.View.RenderAsync(viewContext);
            return sw.ToString();
        }
    }
}
  1. 在Razor页面中添加生成PDF的按钮:
<div class="row mb-3">
    <div class="offset-sm-3 col-sm-3 d-grid">
        <button type="submit" class="btn btn-success" asp-page-handler="GeneratePdf">生成PDF</button>
    </div>
    <div class="col-sm-3 d-grid">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
    <div class="col-sm-3 d-grid">
        <a class="btn btn-outline-primary" href="/Clients/Index" role="button">Cancel</a>
    </div>
</div>

替代方案2:使用RazorEngineCore + PdfSharpCore

如果不想依赖外部工具,可以用纯.NET库实现,先渲染Razor模板为HTML,再转换为PDF。

实现步骤:

  1. 安装NuGet包:
Install-Package RazorEngineCore
Install-Package PdfSharpCore
Install-Package HtmlRenderer.PdfSharpCore
  1. 修改PrintModel添加PDF生成方法:
using RazorEngineCore;
using PdfSharpCore.Pdf;
using System.IO;
using System.Threading.Tasks;

namespace TestApp01.Pages.Clients
{
    public class PrintModel : PageModel
    {
        // 原有属性和方法保持不变...

        public async Task<IActionResult> OnPostGeneratePdf()
        {
            // 读取Razor模板文件内容
            var templatePath = Path.Combine(Directory.GetCurrentDirectory(), "Pages", "Clients", "Print.cshtml");
            var templateContent = await System.IO.File.ReadAllTextAsync(templatePath);

            // 编译并渲染模板
            var razorEngine = new RazorEngine();
            var compiledTemplate = razorEngine.Compile(templateContent);
            var htmlContent = compiledTemplate.Run(this);

            // 将HTML转换为PDF
            var pdfDocument = TheArtOfDev.HtmlRenderer.PdfSharp.PdfGenerator.GeneratePdf(htmlContent, PdfSharpCore.PageSize.A4);

            // 保存到内存流并返回
            using var ms = new MemoryStream();
            pdfDocument.Save(ms);
            ms.Position = 0;
            return File(ms, "application/pdf", $"Client_Details_{clientInfo.id}.pdf");
        }
    }
}

注意事项:

  • 方案1需要服务器安装对应版本的wkhtmltopdf,或者通过NuGet安装运行时包。
  • 渲染HTML时,确保外部资源(CSS、图片)使用绝对路径,或者转为内联资源。
  • 如果页面中有JavaScript动态内容,方案1的wkhtmltopdf支持执行JS,方案2的HtmlRenderer对JS支持有限。

内容的提问来源于stack exchange,提问作者Cyrus Yabut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 22:36:20