跳至頁尾內容
使用IRONPDF

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

在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
$vbLabelText   $csharpLabel

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

接著,在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
$vbLabelText   $csharpLabel

在第一個文件中,我們設置了一個簡單的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()
$vbLabelText   $csharpLabel

如何處理不同的響應型別?

您的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
$vbLabelText   $csharpLabel

在這裡,我們在控制器中新增了一個更靈活的PDF生成端點。 與其總是強制下載文件,GeneratePdf方法允許客戶端選擇他們希望返回結果的方式。 此選項提供靈活性,允許PDF以多種格式顯示:作為可下載文件,直接在瀏覽器中顯示,或編碼為Base64字串以便在API中輕鬆使用。

請求由PdfRequest模型定義,該模型基於早期的HtmlRequest新增了一個ResponseType選項。 簡而言之,這給使用者更多的控制權,讓他們接收PDF時可以更靈活,讓API更具多功能性和使用者友好性。

現在,當我們運行程式時,將在Swagger中看到這個輸出。

如何使用IronPDF建立.NET PDF API:圖4 - Swagger UI

如何實現常見的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
$vbLabelText   $csharpLabel

該端點允許客戶端發送URL,並獲得該網頁的可下載PDF。 當POST /api/pdf/url-to-pdf請求進來時,控制器使用_pdfService在背景中將給定的URL轉換為PDF字節,然後作為文件下載返回它們。 如果轉換過程中出現問題,它會優雅地回應一條清晰的錯誤資訊。

讓我們嘗試使用URL "https://www.apple.com/nz" 測試POST請求。以下是我們獲得的輸出。

輸出

如何使用IronPDF建立.NET PDF API:圖5 - URL PDF輸出

新增自定義水印

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

在這裡,我們只是手動載入本地文件以進行測試。 不過,您可以調整這個,讓您的PDF API生成PDF文件,然後輕鬆地為其新增自定義水印。

水印輸出

如何使用IronPDF建立.NET PDF API:圖6 - 來自上面程式碼範例的水印輸出

如何使用模板新增動態資料

對於實際應用,您經常需要從含有動態資料的模板生成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
$vbLabelText   $csharpLabel

有關使用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
$vbLabelText   $csharpLabel

渲染選項

配置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
$vbLabelText   $csharpLabel

如何保護您的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)()
$vbLabelText   $csharpLabel

對於更高級的驗證場景,考慮:

實際案例:發票生成API

讓我們建立一個實際的發票生成端點,展示一個完整的實現。 此範例展示了一個生產.NET PDF API如何生成具有動態資料的專業發票。

現在開始使用IronPDF。
green arrow pointer

首先,我們會在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
$vbLabelText   $csharpLabel

然後,我們需要為我們的發票生成器建立一個新的服務文件。 在您的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
$vbLabelText   $csharpLabel

最後,您需要建立一個新的控制器以便使用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
$vbLabelText   $csharpLabel

發票輸出

如何使用IronPDF建立.NET PDF API:圖7 - PDF發票輸出

容器部署考量

雖然這個教程重點放在本地開發上,但這裡是對將您的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.
$vbLabelText   $csharpLabel

關於您的.NET PDF API的詳細部署指南,請參閱:

錯誤處理的最佳實踐

為實現更具容錯能力的程式,最佳實踐是實施全域錯誤處理器以提供一致的錯誤響應,例如:

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

對於具體的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? 以下是您的下一步行動:

  1. 開始免費試用 - 在您的開發環境中測試具有完整功能的IronPDF
  2. 探索高級功能 - 查看數位簽名PDF表單和其他高級PDF功能
  3. 有信心的擴展 - 查看生產API需求的許可選項

今天就構建您的.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)一樣即可開箱即用。

Curtis Chau
技術作家

Curtis Chau擁有Carleton大學的電腦科學學士學位,專精於前端開發,擁有Node.js、TypeScript、JavaScript和React的專業知識。Curtis熱衷於建立直觀且美觀的使用者介面,喜愛使用現代框架並建立結構良好、視覺吸引力的手冊。

除了開發,Curtis對物聯網(IoT)有濃厚的興趣,探索創新的方法來整合硬體和軟體。在空閒時間,他喜歡玩遊戲和建立Discord機器人,結合他對技術的熱愛與創造力。

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話