html2pdf中的C#頁面中斷修訂(開發者教程)
在處理現代應用程式時,像您這樣的.NET開發者可能會需要建立一個集中式的PDF生成服務。無論您是在生成發票、報告、憑證還是合約,擁有一個專用的.NET PDF API都可以有效管理PDF文件。 那麼它如何改善您的PDF生成任務呢? 它通過提供一致性、可維護性和擴展性來優化您的桌面和網頁應用。 從未如此輕鬆地管理文件內容、PDF頁面和PDF表單欄位。
在本教程中,您將學習如何使用ASP.NET Core和IronPDF,這個強大的.NET PDF程式庫,來構建一個可投入生產的PDF API。 我們將建立RESTful端點,可以從HTML生成PDF、合併文件、新增水印,並處理您的Web API中的各種實際PDF生成場景。
為什麼要建立一個專用的PDF API?
在深入程式碼之前,讓我們理解為什麼要建立一個專用的PDF API是有意義的:
- 集中化邏輯: 所有PDF生成邏輯集中於一處,使維護和更新更容易
- 微服務架構: 完美適用於需要PDF功能的服務導向架構
- 性能優化: 更容易擴展和優化專用服務以處理大型PDF文件、多頁面和動態資料。
- 語言無關: 任意客戶端應用都可以不考慮程式語言地消耗API
- 一致的輸出: 確保您組織中的所有PDF文件保持一致的文件佈局、段落格式和PDF內容。
準備開始建設了嗎? 下載IronPDF的免費試用,然後跟隨本教程,在您的.NET Framework專案中以編程方式建立PDF文件。
IronPDF:完整的.NET PDF程式庫
IronPDF 被公認為.NET開發者的首選PDF程式庫,提供了一套完整的功能,使在Web API專案中生成PDF變得簡單可靠。 它基於Chrome渲染引擎構建,確保HTML轉PDF轉換像素完美,通常只需幾行程式碼。 在維持所有樣式、JavaScript執行和響應式佈局的同時完成這一切。
IronPDF理想用於.NET PDF API開發的關鍵能力:
- 基於Chrome的渲染: 利用Google Chrome的渲染引擎準確地從HTML內容轉換PDF文件,完全支持嵌入圖像和其他網頁資產
- 豐富的功能集: 支持編輯新的和現有的文件,包含數位簽名、PDF表單、註釋、加密、壓縮等功能
- 建立安全的PDF文件: 使用加密、數位簽名和文件保護管理敏感的PDF內容。
- 多種輸入格式: 使用HTML、URL、圖像和Office文件建立PDF文件
- 高級操作: 合併PDF頁面、拆分文件、應用水印、建立互動式PDF表單,並以編程方式操作PDF文件。
- 跨平台支持: 支持Windows、Linux、macOS、Docker和雲平台
- 性能優化: 異步操作,高效的記憶體管理和快速渲染
如何設置您的PDF文件API專案?
讓我們開始建立一個新的ASP.NET Core Web API專案並安裝必要的包。
先決條件
- .NET 6.0 SDK or later
- Visual Studio 2022 or Visual Studio Code
- Postman 或類似的API測試工具來測試您的PDF REST API
建立專案
首先,讓我們建立一個將建立PDF生成工具的專案。
dotnet new webapi -n PdfApiService
cd PdfApiService
安裝IronPDF
下一步是通過NuGet將IronPDF新增到您的專案中:
dotnet add package IronPdf
或者,在Visual Studio中的NuGet包管理控制台中使用:
Install-Package IronPdf
專案結構
C#開發中的一個重要方面是保持乾淨且結構良好的專案資料夾。 例如:
如何建立您的首個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,該類已經設置了合理的預設值,如頁面邊距和背景渲染,以生成精緻的最終文件。
當控制器傳入原始HTML時,服務使用IronPDF將其渲染成專業質量的PDF,並以字節資料形式返回結果,準備好下載。 此外,它也可以通過直接將URL轉換為PDF來處理整個網頁。
建立控制器
現在是時候為我們的API建立控制器了。 這將提供一個能夠從HTML生成PDF文件的API端點。 然後,它將能夠下載並將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。 當有人通過簡單的POST請求將HTML內容發送到api/pdf/html-to-pdf路由時,PdfController將其轉換為PDF的工作交給專用的服務。
一旦建立了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方法允許客戶端選擇他們希望返回結果的方式。 此選項提供靈活性,允許PDF以多種格式顯示:作為可下載文件,直接在瀏覽器中顯示,或編碼為Base64字串以便在API中輕鬆使用。
請求由PdfRequest模型定義,該模型基於早期的HtmlRequest新增了一個ResponseType選項。 簡而言之,這給使用者更多的控制權,讓他們接收PDF時可以更靈活,讓API更具多功能性和使用者友好性。
現在,當我們運行程式時,將在Swagger中看到這個輸出。

如何實現常見的PDF操作?
讓我們擴展我們的服務以處理各種PDF生成場景:
URL轉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字節,然後作為文件下載返回它們。 如果轉換過程中出現問題,它會優雅地回應一條清晰的錯誤資訊。
讓我們嘗試使用URL "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文件,然後輕鬆地為其新增自定義水印。
水印輸出

如何使用模板新增動態資料
對於實際應用,您經常需要從含有動態資料的模板生成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";
}
[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應用程式。
如何優化性能?
在構建生產PDF API時,性能至關重要。 以下是關鍵的優化策略:
異步操作
在構建涉及I/O操作的專案時,使用異步編程是明智之舉。 特別是當您的PDF內容來自外部資源時,如:
- 下載HTML頁面(RenderUrlAsPdf)
- 通過HTTP獲取圖像、CSS或字體
- 讀寫檔案到磁碟或雲儲存
這些操作可能會阻塞一個執行緒,但使用異步操作可以防止您的API執行緒閒置等待。
範例:
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
渲染選項
配置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速率限制 - 防止濫用並確保公平使用
實際案例:發票生成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 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"]
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"]
The provided code is a Dockerfile written for building and running a .NET application, not C# code. Dockerfiles are not converted to VB.NET as they are not programming language code but configuration scripts for Docker. If you have C# code that needs conversion to VB.NET, please provide that code instead.
關於您的.NET PDF API的詳細部署指南,請參閱:
- IronPDF Docker文件 - 完整的容器化指南
- IronPDF Azure部署 - Azure函式和應用服務
- IronPDF AWS部署 - Lambda和EC2部署
- IronPDF Linux設置 - 特定於Linux的配置
錯誤處理的最佳實踐
為實現更具容錯能力的程式,最佳實踐是實施全域錯誤處理器以提供一致的錯誤響應,例如:
// Middleware/ErrorHandlingMiddleware.cs
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
var response = new
{
error = "An error occurred processing your request",
message = ex.Message
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
// Middleware/ErrorHandlingMiddleware.cs
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
var response = new
{
error = "An error occurred processing your request",
message = ex.Message
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
Imports System.Text.Json
Imports Microsoft.AspNetCore.Http
Imports System.Threading.Tasks
Public Class ErrorHandlingMiddleware
Private ReadOnly _next As RequestDelegate
Public Sub New(next As RequestDelegate)
_next = next
End Sub
Public Async Function InvokeAsync(context As HttpContext) As Task
Try
Await _next(context)
Catch ex As Exception
Await HandleExceptionAsync(context, ex)
End Try
End Function
Private Shared Async Function HandleExceptionAsync(context As HttpContext, ex As Exception) As Task
context.Response.ContentType = "application/json"
context.Response.StatusCode = 500
Dim response = New With {
.error = "An error occurred processing your request",
.message = ex.Message
}
Await context.Response.WriteAsync(JsonSerializer.Serialize(response))
End Function
End Class
對於具體的IronPDF故障排除場景,請參考IronPDF故障排除指南。
總結
您現在已使用ASP.NET Core和IronPDF構建了一個強大的.NET PDF API,它可以處理多種文件生成場景。 此REST API為您的應用程式提供集中化PDF操作的堅實基礎。
關鍵要點:
- IronPDF使在Web API專案中生成PDF變得簡單明瞭,基於其Chrome渲染
- 您可以輕鬆調整您的Web API,以使用IronPDF的高級編輯工具編輯現有的PDF文件
- RESTful設計原則確保您的PDF API直觀且可維護
- 適當的錯誤處理和安全措施對於生產必不可少
- 通過異步操作和快取進行性能優化可提高可擴展性
- 您將能夠支持具有可擴展文件解決方案的桌面和網頁應用
IronPDF讓開發者能夠建立PDF文件、保存PDF文件並有效地轉換HTML,成為現代.NET Framework應用程式中必不可少的PDF文件API。
下一步
準備好了嗎?在您的生產.NET PDF API中實施IronPDF? 以下是您的下一步行動:
今天就構建您的.NET PDF API,並通過IronPDF簡化整個應用程式生態系統中的文件生成!
常見問題
什麼是 .NET PDF API?
.NET PDF API 是一個程式庫,允許開發者在 .NET 應用程式中建立、編輯和提取 PDF 內容。它簡化了複雜的 PDF 任務並確保 PDF 文件的高效管理。
如何讓 .NET PDF API 受益於我的應用程式?
.NET PDF API 可以透過在管理 PDF 文件(如生成發票、報告、證書或合約時)提供一致性、可維護性和可擴展性來增強您的應用程式。
.NET PDF API 的一些常見使用案例是什麼?
.NET PDF API 的常見使用案例包括生成發票、建立報告、製作證書和管理桌面及網頁應用程式中的合約。
IronPDF 如何簡化 PDF 生成任務?
IronPDF 通過提供一個強大的程式庫來簡化 PDF 生成任務,允許輕鬆管理文件內容、PDF 頁面和表單欄位,使應用程式更容易維護和擴展。
IronPDF 可以處理 PDF 表單欄位嗎?
是的,IronPDF 可以有效管理 PDF 表單欄位,允許開發者在 PDF 文件中建立、填充和提取資料。
IronPDF 是否適合桌面和網頁應用程式?
當然,IronPDF 被設計為可以無縫運行於桌面和網頁應用程式,提供一致和可擴展的 PDF 管理解決方案。
IronPDF 為什麼是 .NET 開發者可靠的選擇?
IronPDF 因其易於使用、全面的功能以及精簡 PDF 任務的能力而成為 .NET 開發者的可靠選擇,增強生產力和應用程式性能。
IronPDF 支持 PDF 提取功能嗎?
是的,IronPDF 支持 PDF 提取功能,可有效從 PDF 文件中提取文字、映像和其他資料。
IronPDF 如何在管理 PDF 時提高可擴展性?
IronPDF 提供集中化的 PDF 生成服務來提高可擴展性,能夠在不犧牲性能的情況下應對日益增長的需求,使其成為適合不斷增長應用程式的理想選擇。
IronPDF 為 .NET 應用程式提供了什麼樣的支持?
IronPDF 為 .NET 應用程式提供了廣泛的支持,包括詳細的文件、範例程式碼和響應迅速的支持團隊,協助開發者整合 PDF 功能。
IronPDF是否完全相容.NET 10?
是的,IronPDF 完全相容 .NET 10。它支持 .NET 10 所引入的所有性能、語言和運行時增強,並且在 .NET 10 專案中可以像以往版本(如 .NET 6、7、8 和 9)一樣即可開箱即用。




