如何使用ASP.NET Core和IronPDF构建一个集中式的PDF生成服务
利用 ASP.NET Core 和 IronPDF 构建一个生产就绪的 .NET PDF API,以集中管理 PDF 生成逻辑,通过用于 HTML 转 PDF、合并、添加水印和动态模板处理的 RESTful 端点,实现应用程序间文档创建的一致性。
在开发现代应用程序时,.NET 开发人员通常需要构建一个集中的 PDF 生成服务。无论是生成发票、报告、证书还是合同,拥有一个专用的 .NET PDF API 都能优化您的 PDF 生成工作流程。 它有何帮助?它能在您的桌面和 Web 应用程序中提供一致性、可维护性和可扩展性。 管理文档内容、PDF 页面和 PDF 表单字段变得轻而易举。
在本教程中,您将学习如何使用 ASP.NET Core 和IronPDF (一个强大的 .NET PDF 库)构建可用于生产的 PDF API。 我们将创建 RESTful 接口,用于在您的 Web API 中将 HTML 转换为 PDF、合并文档、添加水印,并处理各种实际的 PDF 场景。
为什么构建一个专门的PDF API?
架构图显示客户端应用程序(Web、桌面、移动)向 PDF 控制器/服务 API 层发送 HTML 内容、URL 或动态数据,该层输出 PDF 文档。
在深入研究代码之前,让我们了解为什么创建一个专门的PDF API是有意义的:
- 集中化逻辑:所有 PDF 生成逻辑集中于一处,简化了维护和更新工作。
- 微服务架构:非常适合面向服务的架构,其中不同应用程序需要 PDF 功能。
- 性能优化:利用异步操作和性能优化技术,更轻松地扩展并优化专用于处理大型 PDF、多页文档及动态数据的专用服务。
- 语言无关:无论使用何种编程语言,任何客户端应用程序均可调用该 API。
- 输出一致性:确保贵组织内所有 PDF 文件在版式、格式和内容上保持一致。
准备好开始构建了吗? 下载IronPDF的免费试用版,并按此教程一起编程实现.NET Framework项目中的PDF文件创建。
是什么让 IronPDF 成为最全面的 .NET PDF 库?
IronPDF 适用于 .NET 主页展示了使用 C# 代码将 HTML 转换为 PDF 的示例,并提供了 HTML 渲染、文件保存和 NuGet 安装命令等功能。
IronPDF是 .NET 开发人员首选的 PDF 库,它提供了一套全面的功能,使 Web API 项目中的 PDF 生成变得简单可靠。 该工具基于 Chrome 渲染引擎构建,仅需几行代码即可实现像素级精准的 HTML 转 PDF 转换,同时完整保留所有样式、JavaScript 执行效果及响应式布局。
使IronPDF成为.NET PDF API开发的理想选择的关键能力有:
- 基于 Chrome 的渲染:采用 Google Chrome 引擎实现精准的 HTML 转 PDF 转换,全面支持嵌入式图像和 Web 资源。
- 丰富的功能集:支持编辑带有数字签名、PDF 表单、批注、加密、压缩等功能的文档。
- 创建安全 PDF:通过加密、数字签名和文档保护来管理敏感内容。
- 多种输入格式:支持使用 HTML、URL、图片和 Office 文档创建 PDF。
- 高级操作:合并页面、拆分文档、添加水印、创建交互式表单,并通过编程方式操作 PDF 文件。
- 跨平台支持:可在 Windows、Linux、macOS、Docker 及云平台上运行。
- 性能优化:支持异步操作、高效的内存管理,并提供渲染延迟选项以实现快速渲染。
如何设置您的PDF文档API项目?
让我们从创建一个新的ASP.NET Core Web API项目并安装必要的包开始。
有哪些先决条件?
- .NET 6.0 SDK或更高版本
- Visual Studio 2022或Visual Studio Code
- 使用 Postman 或类似的 API 测试工具来测试您的 PDF REST API
如何创建该项目?
首先,让我们创建一个项目,用于构建我们的 PDF 生成工具。
dotnet new webapi -n PdfApiService
cd PdfApiService
dotnet new webapi -n PdfApiService
cd PdfApiService
如何安装 IronPDF?
接下来,通过 NuGet 将 IronPDF 添加到您的项目中:
dotnet add package IronPDF
dotnet add package IronPDF
或者,在Visual Studio中使用NuGet包管理器控制台:
Install-Package IronPDF
有关高级安装选项(包括特定平台的软件包、Docker 设置或 Linux 配置),请查阅 IronPDF 安装文档。
我应该采用什么样的项目结构?
良好的 C# 开发需要保持项目文件夹的整洁与结构清晰。 例如:
Visual Studio 解决方案资源管理器显示了 .NET PDF API 服务项目的文件夹结构,其中包含 Controllers、Models 和 Services 目录。
如何创建您的第一个PDF端点?
让我们构建一个简单的端点,将HTML转换为PDF格式。 首先,创建服务接口和实现:
如何创建 PDF 服务?
首先,将以下内容添加到您的 IPdfService.cs 文件中:
public interface IPdfService
{
byte[] GeneratePdfFromHtml(string htmlContent);
byte[] GeneratePdfFromUrl(string url);
}
public interface IPdfService
{
byte[] GeneratePdfFromHtml(string htmlContent);
byte[] GeneratePdfFromUrl(string url);
}
Public Interface IPdfService
Function GeneratePdfFromHtml(htmlContent As String) As Byte()
Function GeneratePdfFromUrl(url As String) As Byte()
End Interface
在 PdfService.cs 文件中,添加以下内容:
using IronPdf;
public class PdfService : IPdfService
{
private readonly ChromePdfRenderer _renderer;
public PdfService()
{
_renderer = new ChromePdfRenderer();
// Configure rendering options for optimal PDF generation in .NET
_renderer.RenderingOptions.MarginTop = 20;
_renderer.RenderingOptions.MarginBottom = 20;
_renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
public byte[] GeneratePdfFromHtml(string htmlContent)
{
// Generate PDF from HTML using the .NET PDF API
var pdf = _renderer.RenderHtmlAsPdf(htmlContent);
return pdf.BinaryData;
}
public byte[] GeneratePdfFromUrl(string url)
{
// Convert URL to PDF in the REST API
var pdf = _renderer.RenderUrlAsPdf(url);
return pdf.BinaryData;
}
}
using IronPdf;
public class PdfService : IPdfService
{
private readonly ChromePdfRenderer _renderer;
public PdfService()
{
_renderer = new ChromePdfRenderer();
// Configure rendering options for optimal PDF generation in .NET
_renderer.RenderingOptions.MarginTop = 20;
_renderer.RenderingOptions.MarginBottom = 20;
_renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
public byte[] GeneratePdfFromHtml(string htmlContent)
{
// Generate PDF from HTML using the .NET PDF API
var pdf = _renderer.RenderHtmlAsPdf(htmlContent);
return pdf.BinaryData;
}
public byte[] GeneratePdfFromUrl(string url)
{
// Convert URL to PDF in the REST API
var pdf = _renderer.RenderUrlAsPdf(url);
return pdf.BinaryData;
}
}
Imports IronPdf
Public Class PdfService
Implements IPdfService
Private ReadOnly _renderer As ChromePdfRenderer
Public Sub New()
_renderer = New ChromePdfRenderer()
' Configure rendering options for optimal PDF generation in .NET
_renderer.RenderingOptions.MarginTop = 20
_renderer.RenderingOptions.MarginBottom = 20
_renderer.RenderingOptions.PrintHtmlBackgrounds = True
End Sub
Public Function GeneratePdfFromHtml(htmlContent As String) As Byte()
' Generate PDF from HTML using the .NET PDF API
Dim pdf = _renderer.RenderHtmlAsPdf(htmlContent)
Return pdf.BinaryData
End Function
Public Function GeneratePdfFromUrl(url As String) As Byte()
' Convert URL to PDF in the REST API
Dim pdf = _renderer.RenderUrlAsPdf(url)
Return pdf.BinaryData
End Function
End Class
PdfService 处理将 HTML 转换为 PDF。 此类使用 IronPDF 的 ChromePdfRenderer,配置页面边距和背景渲染等默认值,以获得专业效果。 如需高级渲染配置,请探索IronPDF 的渲染选项。
当控制器传递原始 HTML 时,该服务将其渲染成高质量的 PDF,并返回字节数据以供下载。 它还可以使用URL 转 PDF 功能,将整个网页直接转换为 PDF。
如何创建控制器?
现在创建 API 的控制器。 这提供了一个可以从 HTML 生成 PDF 文件的端点,并允许您将 PDF 文档下载并保存到您的系统中。
// Controllers/PdfController.cs
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
private readonly IPdfService _pdfService;
public PdfController(IPdfService pdfService)
{
_pdfService = pdfService;
}
[HttpPost("html-to-pdf")]
public IActionResult ConvertHtmlToPdf([FromBody] HtmlRequest request)
{
try
{
var pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent);
// Return as downloadable file
return File(pdfBytes, "application/pdf", "document.pdf");
}
catch (Exception ex)
{
return BadRequest($"Error generating PDF: {ex.Message}");
}
}
}
// Controllers/PdfController.cs
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
private readonly IPdfService _pdfService;
public PdfController(IPdfService pdfService)
{
_pdfService = pdfService;
}
[HttpPost("html-to-pdf")]
public IActionResult ConvertHtmlToPdf([FromBody] HtmlRequest request)
{
try
{
var pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent);
// Return as downloadable file
return File(pdfBytes, "application/pdf", "document.pdf");
}
catch (Exception ex)
{
return BadRequest($"Error generating PDF: {ex.Message}");
}
}
}
Imports Microsoft.AspNetCore.Mvc
<ApiController>
<Route("api/[controller]")>
Public Class PdfController
Inherits ControllerBase
Private ReadOnly _pdfService As IPdfService
Public Sub New(pdfService As IPdfService)
_pdfService = pdfService
End Sub
<HttpPost("html-to-pdf")>
Public Function ConvertHtmlToPdf(<FromBody> request As HtmlRequest) As IActionResult
Try
Dim pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent)
' Return as downloadable file
Return File(pdfBytes, "application/pdf", "document.pdf")
Catch ex As Exception
Return BadRequest($"Error generating PDF: {ex.Message}")
End Try
End Function
End Class
然后,在 HtmlRequest.cs 文件中,添加以下内容:
// Models/HtmlRequest.cs
public class HtmlRequest
{
public string HtmlContent { get; set; }
public string FileName { get; set; } = "document.pdf";
}
// Models/HtmlRequest.cs
public class HtmlRequest
{
public string HtmlContent { get; set; }
public string FileName { get; set; } = "document.pdf";
}
' Models/HtmlRequest.vb
Public Class HtmlRequest
Public Property HtmlContent As String
Public Property FileName As String = "document.pdf"
End Class
这将设置一个 API 端点,用于将 HTML 转换为可下载的 PDF。 当有人向 api/pdf/html-to-pdf 发送 HTML 时,PdfController 会将转换委托给该服务。
创建完成后,控制器会将 PDF 文件作为可下载文件返回。该请求使用 HtmlRequest 模型,其中包含 HTML 代码和一个可选的文件名。 这样一来,客户就可以轻松地发送 HTML 文件并收到精美的 PDF 文件。
如何注册服务?
请更新您的 Program.cs 以注册 PDF 服务:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Register PDF service
builder.Services.AddSingleton<IPdfService, PdfService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Register PDF service
builder.Services.AddSingleton<IPdfService, PdfService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
Imports Microsoft.AspNetCore.Builder
Imports Microsoft.Extensions.DependencyInjection
Dim builder = WebApplication.CreateBuilder(args)
builder.Services.AddControllers()
builder.Services.AddEndpointsApiExplorer()
builder.Services.AddSwaggerGen()
' Register PDF service
builder.Services.AddSingleton(Of IPdfService, PdfService)()
Dim app = builder.Build()
If app.Environment.IsDevelopment() Then
app.UseSwagger()
app.UseSwaggerUI()
End If
app.UseHttpsRedirection()
app.MapControllers()
app.Run()
如何处理不同的响应类型?
您的API应该支持基于客户端需求以不同方式返回PDF:
[HttpPost("generate")]
public IActionResult GeneratePdf([FromBody] PdfRequest request)
{
var pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent);
switch (request.ResponseType?.ToLower())
{
case "base64":
return Ok(new
{
data = Convert.ToBase64String(pdfBytes),
filename = request.FileName
});
case "inline":
return File(pdfBytes, "application/pdf");
default: // download
return File(pdfBytes, "application/pdf", request.FileName);
}
}
[HttpPost("generate")]
public IActionResult GeneratePdf([FromBody] PdfRequest request)
{
var pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent);
switch (request.ResponseType?.ToLower())
{
case "base64":
return Ok(new
{
data = Convert.ToBase64String(pdfBytes),
filename = request.FileName
});
case "inline":
return File(pdfBytes, "application/pdf");
default: // download
return File(pdfBytes, "application/pdf", request.FileName);
}
}
Imports Microsoft.AspNetCore.Mvc
<HttpPost("generate")>
Public Function GeneratePdf(<FromBody> request As PdfRequest) As IActionResult
Dim pdfBytes = _pdfService.GeneratePdfFromHtml(request.HtmlContent)
Select Case request.ResponseType?.ToLower()
Case "base64"
Return Ok(New With {
.data = Convert.ToBase64String(pdfBytes),
.filename = request.FileName
})
Case "inline"
Return File(pdfBytes, "application/pdf")
Case Else ' download
Return File(pdfBytes, "application/pdf", request.FileName)
End Select
End Function
这增加了一个灵活的PDF生成接口。 GeneratePdf 方法不强制下载,而是让客户端选择如何接收结果:下载、在浏览器中内联显示,或以Base64 编码用于 API 调用。
PdfRequest 模型扩展了 HtmlRequest,增加了一个 ResponseType 选项。 这使用户能够控制 PDF 文件的交付方式,从而使 API 更加灵活。有关在不访问文件系统的情况下处理内存中的 PDF 文件,请参阅IronPDF 的内存流文档。
运行程序后,我们会在 Swagger 中看到以下输出:
Swagger UI 文档显示了 PdfApiService 端点,其中包含三个用于 PDF 操作的 POST 方法:html-to-pdf、generate 和 url-to-pdf,以及请求架构。
如何实现常见的PDF操作?
让我们扩展我们的服务以处理各种PDF生成场景:
如何将网址转换为PDF?
[HttpPost("url-to-pdf")]
public async Task<IActionResult> ConvertUrlToPdf([FromBody] UrlRequest request)
{
try
{
var pdfBytes = await Task.Run(() =>
_pdfService.GeneratePdfFromUrl(request.Url));
return File(pdfBytes, "application/pdf",
$"{request.FileName ?? "website"}.pdf");
}
catch (Exception ex)
{
return BadRequest($"Failed to convert URL: {ex.Message}");
}
}
public class UrlRequest
{
public string Url { get; set; }
public string FileName { get; set; }
}
[HttpPost("url-to-pdf")]
public async Task<IActionResult> ConvertUrlToPdf([FromBody] UrlRequest request)
{
try
{
var pdfBytes = await Task.Run(() =>
_pdfService.GeneratePdfFromUrl(request.Url));
return File(pdfBytes, "application/pdf",
$"{request.FileName ?? "website"}.pdf");
}
catch (Exception ex)
{
return BadRequest($"Failed to convert URL: {ex.Message}");
}
}
public class UrlRequest
{
public string Url { get; set; }
public string FileName { get; set; }
}
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Mvc
<HttpPost("url-to-pdf")>
Public Async Function ConvertUrlToPdf(<FromBody> request As UrlRequest) As Task(Of IActionResult)
Try
Dim pdfBytes = Await Task.Run(Function() _pdfService.GeneratePdfFromUrl(request.Url))
Return File(pdfBytes, "application/pdf", $"{If(request.FileName, "website")}.pdf")
Catch ex As Exception
Return BadRequest($"Failed to convert URL: {ex.Message}")
End Try
End Function
Public Class UrlRequest
Public Property Url As String
Public Property FileName As String
End Class
此接口将 URL 转换为可下载的 PDF 文件。 当 POST 请求到达 /api/pdf/url-to-pdf 时,控制器使用 _pdfService 在后台将 URL 转换为 PDF 字节,然后返回以供下载。 如果转换失败,它会显示明确的错误信息。 对于需要身份验证的网站,请查看IronPDF 的登录文档。
我们来试试使用这个网址"https://www.apple.com/nz并测试 POST 请求。以下是我们得到的输出:
输出结果是什么样的?
如何添加自定义水印?
public byte[] AddWatermarkFromFile(string filePath, string watermarkText)
{
// Load PDF directly from file
var pdf = PdfDocument.FromFile(filePath);
pdf.ApplyWatermark(
$"<h1 style='color:red;font-size:72px;'>{watermarkText}</h1>",
75,
IronPdf.Editing.VerticalAlignment.Middle,
IronPdf.Editing.HorizontalAlignment.Center
);
return pdf.BinaryData;
}
public byte[] AddWatermarkFromFile(string filePath, string watermarkText)
{
// Load PDF directly from file
var pdf = PdfDocument.FromFile(filePath);
pdf.ApplyWatermark(
$"<h1 style='color:red;font-size:72px;'>{watermarkText}</h1>",
75,
IronPdf.Editing.VerticalAlignment.Middle,
IronPdf.Editing.HorizontalAlignment.Center
);
return pdf.BinaryData;
}
Imports IronPdf
Public Function AddWatermarkFromFile(filePath As String, watermarkText As String) As Byte()
' Load PDF directly from file
Dim pdf = PdfDocument.FromFile(filePath)
pdf.ApplyWatermark(
$"<h1 style='color:red;font-size:72px;'>{watermarkText}</h1>",
75,
IronPdf.Editing.VerticalAlignment.Middle,
IronPdf.Editing.HorizontalAlignment.Center
)
Return pdf.BinaryData
End Function
此操作会手动加载本地文件进行测试。 您可以进行调整,以便您的 PDF API 生成 PDF,然后轻松应用自定义水印。 有关高级水印选项,包括图像水印和自定义定位,请参阅水印指南。
水印输出效果是什么样的?
这是一份安全文件,带有"机密"水印,水印以对角线方式显示在空白页面上,文件 ID:BA811648DCE1FF2AAA55E7CE 显示于顶部。
如何使用模板添加动态数据?
对于真实世界的应用程序,您通常需要从模板生成带有动态数据的PDFs:
[HttpPost("from-template")]
public IActionResult GenerateFromTemplate([FromBody] TemplateRequest request)
{
// Simple template replacement
var html = request.Template;
foreach (var item in request.Data)
{
html = html.Replace($"{{{{{item.Key}}}}}", item.Value);
}
var pdfBytes = _pdfService.GeneratePdfFromHtml(html);
return File(pdfBytes, "application/pdf", request.FileName);
}
public class TemplateRequest
{
public string Template { get; set; }
public Dictionary<string, string> Data { get; set; }
public string FileName { get; set; } = "document.pdf";
}
[HttpPost("from-template")]
public IActionResult GenerateFromTemplate([FromBody] TemplateRequest request)
{
// Simple template replacement
var html = request.Template;
foreach (var item in request.Data)
{
html = html.Replace($"{{{{{item.Key}}}}}", item.Value);
}
var pdfBytes = _pdfService.GeneratePdfFromHtml(html);
return File(pdfBytes, "application/pdf", request.FileName);
}
public class TemplateRequest
{
public string Template { get; set; }
public Dictionary<string, string> Data { get; set; }
public string FileName { get; set; } = "document.pdf";
}
Imports Microsoft.AspNetCore.Mvc
<HttpPost("from-template")>
Public Function GenerateFromTemplate(<FromBody> request As TemplateRequest) As IActionResult
' Simple template replacement
Dim html As String = request.Template
For Each item In request.Data
html = html.Replace($"{{{{{item.Key}}}}}", item.Value)
Next
Dim pdfBytes As Byte() = _pdfService.GeneratePdfFromHtml(html)
Return File(pdfBytes, "application/pdf", request.FileName)
End Function
Public Class TemplateRequest
Public Property Template As String
Public Property Data As Dictionary(Of String, String)
Public Property FileName As String = "document.pdf"
End Class
对于使用 Razor、Handlebars 或其他引擎的高级模板场景,请查看IronPDF 的 HTML 转 PDF 文档。 您还可以探索CSHTML到PDF转换用于MVC应用程序和Razor到PDF用于Blazor应用程序。 对于无头 Razor 渲染,请参阅CSHTML 无头指南。
如何优化性能?
在构建生产PDF API时,性能至关重要。 以下是关键的优化策略:
为什么要使用异步操作?
当你的项目涉及 I/O 操作时,请使用异步编码。 当您的 PDF 内容来自外部资源时,这一点尤其有用,例如:
- 正在下载 HTML 页面 (
RenderUrlAsPdf) - 通过HTTP获取图像、CSS或字体
- 读取/写入磁盘或云存储的文件
这些操作可能会阻塞线程,但异步操作可以防止 API 线程空闲等待。 有关完整的异步 PDF 生成模式,请参阅异步 PDF 生成指南。
例:
public async Task<byte[]> GeneratePdfFromHtmlAsync(string htmlContent)
{
return await Task.Run(() =>
{
var pdf = _renderer.RenderHtmlAsPdf(htmlContent);
return pdf.BinaryData;
});
}
public async Task<byte[]> GeneratePdfFromHtmlAsync(string htmlContent)
{
return await Task.Run(() =>
{
var pdf = _renderer.RenderHtmlAsPdf(htmlContent);
return pdf.BinaryData;
});
}
Imports System.Threading.Tasks
Public Async Function GeneratePdfFromHtmlAsync(htmlContent As String) As Task(Of Byte())
Return Await Task.Run(Function()
Dim pdf = _renderer.RenderHtmlAsPdf(htmlContent)
Return pdf.BinaryData
End Function)
End Function
对于并行生成 PDF 的场景,可以探索多线程和并行处理技术。
我应该配置哪些渲染选项?
配置IronPDF以获得最佳性能:
_renderer.RenderingOptions.EnableJavaScript = false; // If JS not needed
_renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print;
_renderer.RenderingOptions.RenderDelay = 0; // Remove if no JS
_renderer.RenderingOptions.Timeout = 30; // Set reasonable timeout
_renderer.RenderingOptions.EnableJavaScript = false; // If JS not needed
_renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print;
_renderer.RenderingOptions.RenderDelay = 0; // Remove if no JS
_renderer.RenderingOptions.Timeout = 30; // Set reasonable timeout
如何保护您的PDF API?
对于任何生产API,安全性都是至关重要的。 这里是一个简单的API密钥身份验证方法:
// Middleware/ApiKeyMiddleware.cs
public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private const string ApiKeyHeader = "X-API-Key";
public ApiKeyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (!context.Request.Headers.TryGetValue(ApiKeyHeader, out var apiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("API Key required");
return;
}
// Validate API key (in production, check against database)
var validApiKey = context.RequestServices
.GetRequiredService<IConfiguration>()["ApiKey"];
if (apiKey != validApiKey)
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Invalid API Key");
return;
}
await _next(context);
}
}
// In Program.cs
app.UseMiddleware<ApiKeyMiddleware>();
// Middleware/ApiKeyMiddleware.cs
public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private const string ApiKeyHeader = "X-API-Key";
public ApiKeyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (!context.Request.Headers.TryGetValue(ApiKeyHeader, out var apiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("API Key required");
return;
}
// Validate API key (in production, check against database)
var validApiKey = context.RequestServices
.GetRequiredService<IConfiguration>()["ApiKey"];
if (apiKey != validApiKey)
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Invalid API Key");
return;
}
await _next(context);
}
}
// In Program.cs
app.UseMiddleware<ApiKeyMiddleware>();
Imports Microsoft.AspNetCore.Http
Imports Microsoft.Extensions.Configuration
Imports System.Threading.Tasks
' Middleware/ApiKeyMiddleware.vb
Public Class ApiKeyMiddleware
Private ReadOnly _next As RequestDelegate
Private Const ApiKeyHeader As String = "X-API-Key"
Public Sub New(next As RequestDelegate)
_next = next
End Sub
Public Async Function InvokeAsync(context As HttpContext) As Task
Dim apiKey As String = Nothing
If Not context.Request.Headers.TryGetValue(ApiKeyHeader, apiKey) Then
context.Response.StatusCode = 401
Await context.Response.WriteAsync("API Key required")
Return
End If
' Validate API key (in production, check against database)
Dim validApiKey As String = context.RequestServices.GetRequiredService(Of IConfiguration)()("ApiKey")
If apiKey <> validApiKey Then
context.Response.StatusCode = 403
Await context.Response.WriteAsync("Invalid API Key")
Return
End If
Await _next(context)
End Function
End Class
' In Program.vb
app.UseMiddleware(Of ApiKeyMiddleware)()
对于高级身份验证场景,请考虑:
- JWT 身份验证 - API 身份验证的行业标准
- OAuth 2.0 - 用于第三方集成
- Azure AD集成 - 企业身份验证
- API速率限制 - 防止滥用并确保公平使用
- HTTP 标头- 自定义标头配置以增强安全性
如何构建一个实际应用的发票生成 API?
让我们构建一个实用的发票生成端点,以演示完整的实现过程。 本示例展示了生产环境中的 .NET PDF API 如何使用动态数据生成专业发票。
首先,在 Models 文件夹中创建一个新文件。 在这里,我把我的命名为Invoice.cs。 然后添加以下代码:
public class Invoice
{
public string InvoiceNumber { get; set; }
public DateTime Date { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public List<InvoiceItem> Items { get; set; }
public decimal Tax { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
}
public class Invoice
{
public string InvoiceNumber { get; set; }
public DateTime Date { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public List<InvoiceItem> Items { get; set; }
public decimal Tax { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
}
Public Class Invoice
Public Property InvoiceNumber As String
Public Property [Date] As DateTime
Public Property CustomerName As String
Public Property CustomerAddress As String
Public Property Items As List(Of InvoiceItem)
Public Property Tax As Decimal
End Class
Public Class InvoiceItem
Public Property Description As String
Public Property Quantity As Integer
Public Property UnitPrice As Decimal
Public ReadOnly Property Total As Decimal
Get
Return Quantity * UnitPrice
End Get
End Property
End Class
接下来,为发票生成器创建一个新的服务文件。 在您的Services文件夹中,添加以下代码。 我创建了一个名为 InvoiceService.cs 的文件。 这段代码用于处理发票 PDF 的样式和布局:
public class InvoiceService
{
private readonly ChromePdfRenderer _renderer;
public InvoiceService()
{
_renderer = new ChromePdfRenderer();
_renderer.RenderingOptions.MarginTop = 10;
_renderer.RenderingOptions.MarginBottom = 10;
_renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
public byte[] GenerateInvoice(Invoice invoice)
{
var html = BuildInvoiceHtml(invoice);
// Add footer with page numbers
_renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
MaxHeight = 15,
HtmlFragment = "<center><i>{page} of {total-pages}</i></center>",
DrawDividerLine = true
};
var pdf = _renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
private string BuildInvoiceHtml(Invoice invoice)
{
var subtotal = invoice.Items.Sum(i => i.Total);
var taxAmount = subtotal * (invoice.Tax / 100);
var total = subtotal + taxAmount;
var itemsHtml = string.Join("", invoice.Items.Select(item =>
$@"<tr>
<td>{item.Description}</td>
<td class='text-center'>{item.Quantity}</td>
<td class='text-right'>${item.UnitPrice:F2}</td>
<td class='text-right'>${item.Total:F2}</td>
</tr>"));
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{
background-color: #f8f9fa;
padding: 20px;
margin-bottom: 20px;
}}
table {{
width: 100%;
border-collapse: collapse;
}}
th, td {{
padding: 10px;
border-bottom: 1px solid #ddd;
}}
th {{
background-color: #007bff;
color: white;
}}
.text-right {{ text-align: right; }}
.text-center {{ text-align: center; }}
.total-section {{
margin-top: 20px;
text-align: right;
}}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{invoice.InvoiceNumber}</h1>
<p>Date: {invoice.Date:yyyy-MM-dd}</p>
</div>
<div>
<h3>Bill To:</h3>
<p>{invoice.CustomerName}<br/>{invoice.CustomerAddress}</p>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{itemsHtml}
</tbody>
</table>
<div class='total-section'>
<p>Subtotal: ${subtotal:F2}</p>
<p>Tax ({invoice.Tax}%): ${taxAmount:F2}</p>
<h3>Total: ${total:F2}</h3>
</div>
</body>
</html>";
}
}
public class InvoiceService
{
private readonly ChromePdfRenderer _renderer;
public InvoiceService()
{
_renderer = new ChromePdfRenderer();
_renderer.RenderingOptions.MarginTop = 10;
_renderer.RenderingOptions.MarginBottom = 10;
_renderer.RenderingOptions.PrintHtmlBackgrounds = true;
}
public byte[] GenerateInvoice(Invoice invoice)
{
var html = BuildInvoiceHtml(invoice);
// Add footer with page numbers
_renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
MaxHeight = 15,
HtmlFragment = "<center><i>{page} of {total-pages}</i></center>",
DrawDividerLine = true
};
var pdf = _renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
private string BuildInvoiceHtml(Invoice invoice)
{
var subtotal = invoice.Items.Sum(i => i.Total);
var taxAmount = subtotal * (invoice.Tax / 100);
var total = subtotal + taxAmount;
var itemsHtml = string.Join("", invoice.Items.Select(item =>
$@"<tr>
<td>{item.Description}</td>
<td class='text-center'>{item.Quantity}</td>
<td class='text-right'>${item.UnitPrice:F2}</td>
<td class='text-right'>${item.Total:F2}</td>
</tr>"));
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{
background-color: #f8f9fa;
padding: 20px;
margin-bottom: 20px;
}}
table {{
width: 100%;
border-collapse: collapse;
}}
th, td {{
padding: 10px;
border-bottom: 1px solid #ddd;
}}
th {{
background-color: #007bff;
color: white;
}}
.text-right {{ text-align: right; }}
.text-center {{ text-align: center; }}
.total-section {{
margin-top: 20px;
text-align: right;
}}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{invoice.InvoiceNumber}</h1>
<p>Date: {invoice.Date:yyyy-MM-dd}</p>
</div>
<div>
<h3>Bill To:</h3>
<p>{invoice.CustomerName}<br/>{invoice.CustomerAddress}</p>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{itemsHtml}
</tbody>
</table>
<div class='total-section'>
<p>Subtotal: ${subtotal:F2}</p>
<p>Tax ({invoice.Tax}%): ${taxAmount:F2}</p>
<h3>Total: ${total:F2}</h3>
</div>
</body>
</html>";
}
}
Imports System
Imports System.Linq
Public Class InvoiceService
Private ReadOnly _renderer As ChromePdfRenderer
Public Sub New()
_renderer = New ChromePdfRenderer()
_renderer.RenderingOptions.MarginTop = 10
_renderer.RenderingOptions.MarginBottom = 10
_renderer.RenderingOptions.PrintHtmlBackgrounds = True
End Sub
Public Function GenerateInvoice(invoice As Invoice) As Byte()
Dim html = BuildInvoiceHtml(invoice)
' Add footer with page numbers
_renderer.RenderingOptions.HtmlFooter = New HtmlHeaderFooter With {
.MaxHeight = 15,
.HtmlFragment = "<center><i>{page} of {total-pages}</i></center>",
.DrawDividerLine = True
}
Dim pdf = _renderer.RenderHtmlAsPdf(html)
Return pdf.BinaryData
End Function
Private Function BuildInvoiceHtml(invoice As Invoice) As String
Dim subtotal = invoice.Items.Sum(Function(i) i.Total)
Dim taxAmount = subtotal * (invoice.Tax / 100)
Dim total = subtotal + taxAmount
Dim itemsHtml = String.Join("", invoice.Items.Select(Function(item) _
$"<tr>
<td>{item.Description}</td>
<td class='text-center'>{item.Quantity}</td>
<td class='text-right'>${item.UnitPrice:F2}</td>
<td class='text-right'>${item.Total:F2}</td>
</tr>"))
Return $"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{
background-color: #f8f9fa;
padding: 20px;
margin-bottom: 20px;
}}
table {{
width: 100%;
border-collapse: collapse;
}}
th, td {{
padding: 10px;
border-bottom: 1px solid #ddd;
}}
th {{
background-color: #007bff;
color: white;
}}
.text-right {{ text-align: right; }}
.text-center {{ text-align: center; }}
.total-section {{
margin-top: 20px;
text-align: right;
}}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{invoice.InvoiceNumber}</h1>
<p>Date: {invoice.Date:yyyy-MM-dd}</p>
</div>
<div>
<h3>Bill To:</h3>
<p>{invoice.CustomerName}<br/>{invoice.CustomerAddress}</p>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{itemsHtml}
</tbody>
</table>
<div class='total-section'>
<p>Subtotal: ${subtotal:F2}</p>
<p>Tax ({invoice.Tax}%): ${taxAmount:F2}</p>
<h3>Total: ${total:F2}</h3>
</div>
</body>
</html>"
End Function
End Class
最后,创建一个新的控制器,用于使用 API 访问和创建发票:
[ApiController]
[Route("api/[controller]")]
public class InvoiceController : ControllerBase
{
private readonly InvoiceService _invoiceService;
public InvoiceController(InvoiceService invoiceService)
{
_invoiceService = invoiceService;
}
[HttpPost("generate")]
public IActionResult GenerateInvoice([FromBody] Invoice invoice)
{
try
{
var pdfBytes = _invoiceService.GenerateInvoice(invoice);
var fileName = $"Invoice_{invoice.InvoiceNumber}.pdf";
return File(pdfBytes, "application/pdf", fileName);
}
catch (Exception ex)
{
return StatusCode(500, $"Error generating invoice: {ex.Message}");
}
}
}
[ApiController]
[Route("api/[controller]")]
public class InvoiceController : ControllerBase
{
private readonly InvoiceService _invoiceService;
public InvoiceController(InvoiceService invoiceService)
{
_invoiceService = invoiceService;
}
[HttpPost("generate")]
public IActionResult GenerateInvoice([FromBody] Invoice invoice)
{
try
{
var pdfBytes = _invoiceService.GenerateInvoice(invoice);
var fileName = $"Invoice_{invoice.InvoiceNumber}.pdf";
return File(pdfBytes, "application/pdf", fileName);
}
catch (Exception ex)
{
return StatusCode(500, $"Error generating invoice: {ex.Message}");
}
}
}
Imports Microsoft.AspNetCore.Mvc
<ApiController>
<Route("api/[controller]")>
Public Class InvoiceController
Inherits ControllerBase
Private ReadOnly _invoiceService As InvoiceService
Public Sub New(invoiceService As InvoiceService)
_invoiceService = invoiceService
End Sub
<HttpPost("generate")>
Public Function GenerateInvoice(<FromBody> invoice As Invoice) As IActionResult
Try
Dim pdfBytes = _invoiceService.GenerateInvoice(invoice)
Dim fileName = $"Invoice_{invoice.InvoiceNumber}.pdf"
Return File(pdfBytes, "application/pdf", fileName)
Catch ex As Exception
Return StatusCode(500, $"Error generating invoice: {ex.Message}")
End Try
End Function
End Class
对于高级发票功能,可以考虑添加条形码、二维码、页码和自定义页眉/页脚。 您还可以实施PDF/A 合规性以进行长期存档,或与电子签名工作流程集成。
发票输出格式是什么样的?
! PDF格式发票文件,发票编号INV-1023,日期为2025年8月22日,包含一项"杂项"项目,总额为495.00美元(含税)。
容器部署需要考虑哪些因素?
虽然本教程侧重于本地开发,但这里是关于将您的PDF API容器化的简要概述:
如何创建基本的 Dockerfile?
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["PdfApiService.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet build -c Release -o /app/build
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
# IronPDF requires additional dependencies on Linux
RUN apt-get update && apt-get install -y \
libgdiplus \
libc6-dev \
libx11-dev \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["dotnet", "PdfApiService.dll"]
有关您的.NET PDF API的详细部署指南,请参阅:
- IronPDF Docker文档 - 完整的容器化指南
- IronPDF Azure部署 - Azure Functions和App Service
- IronPDF AWS部署 - Lambda和EC2部署
- IronPDF Linux设置 - Linux特定配置 以远程容器方式运行- IronPDF 引擎容器化 原生引擎与远程引擎- 部署架构选项
错误处理的最佳实践有哪些?
对于容错程序,应实现全局错误处理程序,以确保错误响应的一致性:
// Middleware/ErrorHandlingMiddleware.cs
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred processing request {Path}", context.Request.Path);
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = ex switch
{
ArgumentNullException => 400,
UnauthorizedAccessException => 401,
_ => 500
};
var response = new
{
error = "An error occurred processing your request",
message = ex.Message,
statusCode = context.Response.StatusCode
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
// Middleware/ErrorHandlingMiddleware.cs
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred processing request {Path}", context.Request.Path);
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = ex switch
{
ArgumentNullException => 400,
UnauthorizedAccessException => 401,
_ => 500
};
var response = new
{
error = "An error occurred processing your request",
message = ex.Message,
statusCode = context.Response.StatusCode
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
' Middleware/ErrorHandlingMiddleware.vb
Public Class ErrorHandlingMiddleware
Private ReadOnly _next As RequestDelegate
Private ReadOnly _logger As ILogger(Of ErrorHandlingMiddleware)
Public Sub New(ByVal [next] As RequestDelegate, ByVal logger As ILogger(Of ErrorHandlingMiddleware))
_next = [next]
_logger = logger
End Sub
Public Async Function InvokeAsync(ByVal context As HttpContext) As Task
Try
Await _next(context)
Catch ex As Exception
_logger.LogError(ex, "An error occurred processing request {Path}", context.Request.Path)
Await HandleExceptionAsync(context, ex)
End Try
End Function
Private Shared Async Function HandleExceptionAsync(ByVal context As HttpContext, ByVal ex As Exception) As Task
context.Response.ContentType = "application/json"
context.Response.StatusCode = If(TypeOf ex Is ArgumentNullException, 400, If(TypeOf ex Is UnauthorizedAccessException, 401, 500))
Dim response = New With {
.error = "An error occurred processing your request",
.message = ex.Message,
.statusCode = context.Response.StatusCode
}
Await context.Response.WriteAsync(JsonSerializer.Serialize(response))
End Function
End Class
有关 IronPDF 的具体故障排除方案,请参阅: 快速故障排除指南 工程支持指南 自定义日志配置 防止内存泄漏 性能优化技巧
准备好构建您的生产级 .NET PDF API 了吗?
您现在已经使用 ASP.NET Core 和 IronPDF 构建了一个强大的 .NET PDF API,可以处理各种文档生成场景。 这个REST API为您的应用程序提供集中化PDF操作的坚实基础。
关键要点:
- IronPDF 通过基于 Chrome 的渲染,使 Web API 项目中的 PDF 生成变得简单。
- 使用 IronPDF 的高级编辑工具轻松编辑现有 PDF 文件。
- RESTful 设计原则确保您的 PDF API 直观易用且易于维护。 适当的错误处理和安全措施对于生产至关重要。 通过异步操作和缓存进行性能优化,提高了可扩展性。
- 全面支持桌面和 Web 应用程序,并提供可扩展的文档解决方案。
IronPDF 使开发人员能够高效地创建 PDF、保存 PDF 文件和转换 HTML,使其成为现代 .NET Framework 应用程序必不可少的 PDF API。
下一步是什么?
准备好在您的生产.NET PDF API中实施IronPDF了吗? 以下是您的下一步操作:
1.开始免费试用- 在您的开发环境中测试 IronPDF 的全部功能。 2.探索高级功能- 查看数字签名、 PDF 表单、 PDF/A 合规性、元数据管理和其他高级 PDF 功能。 3.自信扩展- 查看满足您生产 API 需求的许可选项,包括扩展和升级。
今天就构建您的.NET PDF API,并通过IronPDF在整个应用程序生态系统中简化文档生成!
常见问题解答
IronPDF 在 .NET 应用程序中的用途是什么?
IronPDF 用于在 .NET 应用程序中生成、处理和转换 PDF,是创建集中式 PDF 服务的理想工具。
IronPDF 如何与 ASP.NET Core 集成?
通过安装 IronPDF NuGet 包,可以将 IronPDF 与 ASP.NET Core 集成,从而构建集中式 PDF 生成服务。
IronPDF 生成 PDF 的主要功能是什么?
IronPDF 的主要功能包括 HTML 到 PDF 的转换、合并和拆分 PDF、添加页眉和页脚以及提取文本和图像。
IronPDF 能否处理复杂的 PDF 布局?
是的,IronPDF 能够将 HTML 和 CSS 内容转换为准确渲染的 PDF 文档,因此可以处理复杂的 PDF 布局。
是否可以使用 IronPDF 定制 PDF 创建?
是的,IronPDF 允许在创建 PDF 时进行自定义,包括设置页面大小、页边距以及添加水印或注释。
IronPDF 支持 PDF 安全功能吗?
IronPDF 支持密码保护和加密等 PDF 安全功能,以确保生成的 PDF 文档的安全。
IronPDF 可将哪些格式转换为 PDF?
IronPDF 可以将各种格式的文件转换成 PDF,包括 HTML、URL 和 ASPX 文件,使其成为适用于不同用例的多功能工具。
IronPDF 如何处理大规模 PDF 生成?
IronPDF 针对性能进行了优化,可在 .NET 应用程序中高效处理大规模 PDF 生成任务。
IronPDF 可以用于云应用吗?
是的,IronPDF 可用于基于云的应用程序,支持在 Azure 和 AWS 等平台上部署可扩展的 PDF 服务。
IronPDF for .NET 支持哪些版本?
IronPDF 支持多个 .NET 版本,包括 .NET Core 和 .NET Framework,确保与各种项目兼容。



