diff --git a/DocumentOperator.API/Controllers/PdfAttachmentController.cs b/DocumentOperator.API/Controllers/PdfAttachmentController.cs index a3f4324..a6d31af 100644 --- a/DocumentOperator.API/Controllers/PdfAttachmentController.cs +++ b/DocumentOperator.API/Controllers/PdfAttachmentController.cs @@ -1,5 +1,6 @@ using DocumentOperator.Application.CheckPdfAttachments.Queries; using DocumentOperator.Application.Common.DTOs; +using DocumentOperator.Domain.Common.Exceptions; using MediatR; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -33,13 +34,11 @@ public class PdfAttachmentController(IMediator mediator) : ControllerBase IFormFile file, CancellationToken cancellationToken) { - // Convert IFormFile to byte array - using var memoryStream = new MemoryStream(); - await file.CopyToAsync(memoryStream, cancellationToken); - byte[] pdfBytes = memoryStream.ToArray(); + // Use IFormFile stream directly (no intermediate byte[] conversion) + using var pdfStream = file.OpenReadStream(); // Send query to MediatR (ValidationBehavior runs automatically) - var query = new CheckPdfAttachmentsQuery { PdfBytes = pdfBytes }; + var query = new CheckPdfAttachmentsQuery { PdfStream = pdfStream }; var result = await mediator.Send(query, cancellationToken); return Ok(result); @@ -64,8 +63,21 @@ public class PdfAttachmentController(IMediator mediator) : ControllerBase [FromBody] CheckPdfAttachmentsRequest request, CancellationToken cancellationToken) { + // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) + byte[] pdfBytes; + try + { + pdfBytes = Convert.FromBase64String(request.Base64Pdf); + } + catch (FormatException ex) + { + throw new BadRequestException("Invalid Base64 format: " + ex.Message); + } + + using var pdfStream = new MemoryStream(pdfBytes); + // Send query to MediatR (ValidationBehavior runs automatically) - var query = new CheckPdfAttachmentsQuery { Base64Pdf = request.Base64Pdf }; + var query = new CheckPdfAttachmentsQuery { PdfStream = pdfStream }; var result = await mediator.Send(query, cancellationToken); return Ok(result); diff --git a/DocumentOperator.API/Controllers/PdfValidationController.cs b/DocumentOperator.API/Controllers/PdfValidationController.cs index b6a63cc..66f6428 100644 --- a/DocumentOperator.API/Controllers/PdfValidationController.cs +++ b/DocumentOperator.API/Controllers/PdfValidationController.cs @@ -1,6 +1,7 @@ using DocumentOperator.Application.Common.DTOs; using DocumentOperator.Application.ValidatePdf.Queries; using DocumentOperator.Application.ValidatePdfA.Queries; +using DocumentOperator.Domain.Common.Exceptions; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -42,13 +43,11 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase }); } - // Convert IFormFile to byte array - using var memoryStream = new MemoryStream(); - await file.CopyToAsync(memoryStream, cancellationToken); - byte[] pdfBytes = memoryStream.ToArray(); - + // Use IFormFile stream directly (no intermediate byte[] conversion) + using var pdfStream = file.OpenReadStream(); + // Direct pass-through to MediatR - var query = new ValidatePdfQuery { PdfBytes = pdfBytes }; + var query = new ValidatePdfQuery { PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); return Ok(result); @@ -57,7 +56,7 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase /// /// Validates a PDF document and returns metadata (Base64 JSON) /// - /// PDF as Base64 string + /// Request containing Base64-encoded PDF /// Cancellation token /// PDF metadata (page count, file size, PDF version, attachments) /// PDF is valid, metadata returned @@ -69,10 +68,24 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] public async Task ValidateFromBase64( - [FromBody] ValidatePdfQuery query, + [FromBody] ValidatePdfBase64Request request, CancellationToken cancellationToken) { + // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) + byte[] pdfBytes; + try + { + pdfBytes = Convert.FromBase64String(request.Base64Pdf); + } + catch (FormatException ex) + { + throw new BadRequestException("Invalid Base64 format: " + ex.Message); + } + + using var pdfStream = new MemoryStream(pdfBytes); + // Direct pass-through to MediatR + var query = new ValidatePdfQuery { PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); return Ok(result); @@ -106,13 +119,11 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase }); } - // Convert IFormFile to byte array - using var memoryStream = new MemoryStream(); - await file.CopyToAsync(memoryStream, cancellationToken); - byte[] pdfBytes = memoryStream.ToArray(); - + // Use IFormFile stream directly (no intermediate byte[] conversion) + using var pdfStream = file.OpenReadStream(); + // Direct pass-through to MediatR - var query = new ValidatePdfAQuery { PdfBytes = pdfBytes }; + var query = new ValidatePdfAQuery { PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); return Ok(result); @@ -121,7 +132,7 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase /// /// Validates a PDF/A document and checks conformance level (Base64 JSON) /// - /// PDF as Base64 string + /// Request containing Base64-encoded PDF /// Cancellation token /// PDF/A metadata (conformance level, errors, warnings) /// PDF/A validation completed, results returned @@ -133,12 +144,50 @@ public class PdfValidationController(IMediator Mediator) : ControllerBase [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] public async Task ValidatePdfAFromBase64( - [FromBody] ValidatePdfAQuery query, + [FromBody] ValidatePdfABase64Request request, CancellationToken cancellationToken) { + // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) + byte[] pdfBytes; + try + { + pdfBytes = Convert.FromBase64String(request.Base64Pdf); + } + catch (FormatException ex) + { + throw new BadRequestException("Invalid Base64 format: " + ex.Message); + } + + using var pdfStream = new MemoryStream(pdfBytes); + // Direct pass-through to MediatR + var query = new ValidatePdfAQuery { PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); return Ok(result); } } + +/// +/// Request DTO for Base64-encoded PDF validation +/// +public record ValidatePdfBase64Request +{ + /// + /// PDF document encoded as Base64 string + /// + /// JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c... + public string Base64Pdf { get; init; } = string.Empty; +} + +/// +/// Request DTO for Base64-encoded PDF/A validation +/// +public record ValidatePdfABase64Request +{ + /// + /// PDF document encoded as Base64 string + /// + /// JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c... + public string Base64Pdf { get; init; } = string.Empty; +} diff --git a/DocumentOperator.API/Controllers/SwissQrCodeController.cs b/DocumentOperator.API/Controllers/SwissQrCodeController.cs index 3756ded..4770f9c 100644 --- a/DocumentOperator.API/Controllers/SwissQrCodeController.cs +++ b/DocumentOperator.API/Controllers/SwissQrCodeController.cs @@ -1,5 +1,6 @@ using DocumentOperator.Application.Common.DTOs; using DocumentOperator.Application.SwissQrCode.Queries; +using DocumentOperator.Domain.Common.Exceptions; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -17,7 +18,7 @@ public class SwissQrCodeController(IMediator Mediator) : ControllerBase /// Extracts Swiss QR Code from the last page of a PDF document (multipart/form-data) /// /// PDF file containing Swiss QR Code - /// + /// If true, returns raw QR text lines instead of parsed Bill object /// Cancellation token /// Swiss QR Code data (IBAN, amount, creditor, debtor, reference, etc.) /// Swiss QR Code extracted successfully @@ -43,15 +44,13 @@ public class SwissQrCodeController(IMediator Mediator) : ControllerBase Status = StatusCodes.Status400BadRequest }); - // Convert IFormFile to byte array - using var memoryStream = new MemoryStream(); - await file.CopyToAsync(memoryStream, cancellationToken); - byte[] pdfBytes = memoryStream.ToArray(); + // Use IFormFile stream directly (no intermediate byte[] conversion) + using var pdfStream = file.OpenReadStream(); // Direct pass-through to MediatR var query = new ExtractSwissQrCodeQuery { - PdfBytes = pdfBytes + PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); @@ -61,8 +60,8 @@ public class SwissQrCodeController(IMediator Mediator) : ControllerBase /// /// Extracts Swiss QR Code from the last page of a PDF document (Base64 JSON) /// - /// References array + PDF as Base64 string - /// + /// Request containing Base64-encoded PDF + /// If true, returns raw QR text lines instead of parsed Bill object /// Cancellation token /// Swiss QR Code data (IBAN, amount, creditor, debtor, reference, etc.) /// Swiss QR Code extracted successfully @@ -76,13 +75,39 @@ public class SwissQrCodeController(IMediator Mediator) : ControllerBase [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] public async Task ExtractFromBase64( - [FromBody] ExtractSwissQrCodeQuery query, + [FromBody] ExtractSwissQrCodeBase64Request request, [FromQuery] bool raw = false, CancellationToken cancellationToken = default) { + // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) + byte[] pdfBytes; + try + { + pdfBytes = Convert.FromBase64String(request.Base64Pdf); + } + catch (FormatException ex) + { + throw new BadRequestException("Invalid Base64 format: " + ex.Message); + } + + using var pdfStream = new MemoryStream(pdfBytes); + // Direct pass-through to MediatR + var query = new ExtractSwissQrCodeQuery { PdfStream = pdfStream }; var result = await Mediator.Send(query, cancellationToken); return Ok(raw ? result.RawLines : result.Bill); } } + +/// +/// Request DTO for Base64-encoded Swiss QR Code extraction +/// +public record ExtractSwissQrCodeBase64Request +{ + /// + /// PDF document encoded as Base64 string + /// + /// JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c... + public required string Base64Pdf { get; init; } +}