From a242458d2fa0d57c5984a0c870fa3ce4110da85a Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 30 Jul 2026 10:47:48 +0200 Subject: [PATCH] Add support for XML file output in ZUGFeRD extraction Enhanced `ExtractZugferdFromFile` and `ExtractZugferdFromBase64` methods to support output as an XML file (`application/xml`) or JSON (default). Introduced `asFile` and `format` query parameters to control the output format. Updated XML documentation and `ProducesResponseType` attributes to reflect these changes. Default values for `CancellationToken` were set to `default` for improved usability. --- .../Controllers/ZugferdController.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/DocumentOperator.API/Controllers/ZugferdController.cs b/DocumentOperator.API/Controllers/ZugferdController.cs index a5761a3..bcaabb6 100644 --- a/DocumentOperator.API/Controllers/ZugferdController.cs +++ b/DocumentOperator.API/Controllers/ZugferdController.cs @@ -89,8 +89,9 @@ public class ZugferdController(IMediator mediator) : ControllerBase /// Supports multipart/form-data file upload. /// /// The PDF file to extract ZUGFeRD from + /// if true, 'file' (returns XML file directly); otherwise output format: 'json' (default, returns metadata + XML content) /// Cancellation token - /// ZUGFeRD XML content and metadata + /// ZUGFeRD XML content and metadata (JSON) or XML file (application/xml) /// ZUGFeRD XML extracted successfully /// Invalid input (file missing, not a PDF, or corrupted) /// PDF contains no ZUGFeRD XML @@ -98,12 +99,14 @@ public class ZugferdController(IMediator mediator) : ControllerBase [HttpPost("extract")] [Consumes("multipart/form-data")] [ProducesResponseType(typeof(ZugferdExtractionResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] public async Task ExtractZugferdFromFile( IFormFile file, - CancellationToken cancellationToken) + [FromQuery] bool asFile = true, + CancellationToken cancellationToken = default) { // Use IFormFile stream directly (no intermediate byte[] conversion) using var pdfStream = file.OpenReadStream(); @@ -112,6 +115,13 @@ public class ZugferdController(IMediator mediator) : ControllerBase var command = new ExtractZugferdCommand { PdfStream = pdfStream }; var result = await mediator.Send(command, cancellationToken); + // Return as file or JSON based on format parameter + if (asFile) + { + byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(result.XmlContent); + return File(xmlBytes, "application/xml", result.FileName); + } + return Ok(result); } @@ -120,8 +130,9 @@ public class ZugferdController(IMediator mediator) : ControllerBase /// Supports Base64-encoded PDF via JSON payload. /// /// Request containing Base64-encoded PDF + /// Output format: 'json' (default, returns metadata + XML content) or 'file' (returns XML file directly) /// Cancellation token - /// ZUGFeRD XML content and metadata + /// ZUGFeRD XML content and metadata (JSON) or XML file (application/xml) /// ZUGFeRD XML extracted successfully /// Invalid input (Base64 format error, not a PDF, or corrupted) /// PDF contains no ZUGFeRD XML @@ -129,12 +140,14 @@ public class ZugferdController(IMediator mediator) : ControllerBase [HttpPost("extract")] [Consumes("application/json")] [ProducesResponseType(typeof(ZugferdExtractionResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] public async Task ExtractZugferdFromBase64( [FromBody] ExtractZugferdRequest request, - CancellationToken cancellationToken) + [FromQuery] string format = "json", + CancellationToken cancellationToken = default) { // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) byte[] pdfBytes; @@ -153,6 +166,13 @@ public class ZugferdController(IMediator mediator) : ControllerBase var command = new ExtractZugferdCommand { PdfStream = pdfStream }; var result = await mediator.Send(command, cancellationToken); + // Return as file or JSON based on format parameter + if (format.Equals("file", StringComparison.OrdinalIgnoreCase)) + { + byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(result.XmlContent); + return File(xmlBytes, "application/xml", result.FileName); + } + return Ok(result); } }