From 9f1e57c13f52eae1dacc69e29bad021db379aade Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 4 Oct 2026 12:41:05 +0200 Subject: [PATCH] Return File in ASP.NET Core Web API: move the sample to .NET 10 Retarget both projects to net10.0, replace Swashbuckle with Microsoft.AspNetCore.OpenApi, swap Moq for NSubstitute, bump the test packages, assert the concrete file result types in the tests, and add the range-processing action and the minimal API endpoint the article now shows. --- .../ReturnFileWebApi/Assets/Image.cs | 9 ++- .../Controllers/DownloadsController.cs | 61 ++++++++-------- .../Interface/IFileService.cs | 11 ++- .../ReturnFileWebApi/Program.cs | 11 +-- .../Properties/launchSettings.json | 4 +- .../ReturnFileWebApi/ReturnFileWebApi.csproj | 4 +- .../ReturnFileWebApi/Services/FileService.cs | 24 +++---- .../ReturnFileWebApi/Tests/Tests.cs | 71 ++++++++++--------- .../ReturnFileWebApi/Tests/Tests.csproj | 12 ++-- 9 files changed, 104 insertions(+), 103 deletions(-) diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Assets/Image.cs b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Assets/Image.cs index 58468d06fb..d4188c7037 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Assets/Image.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Assets/Image.cs @@ -1,7 +1,6 @@ -namespace ReturnFileWebApi.Assets +namespace ReturnFileWebApi.Assets; + +public static class Image { - public static class Image - { - public const string Base64Image = "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"; - } + public const string Base64Image = "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"; } diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Controllers/DownloadsController.cs b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Controllers/DownloadsController.cs index 19fa4a4009..c8c5ab2f88 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Controllers/DownloadsController.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Controllers/DownloadsController.cs @@ -1,36 +1,37 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using ReturnFileWebApi.Interface; -namespace ReturnFileWebApi.Controllers +namespace ReturnFileWebApi.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class DownloadsController(IFileService fileService) : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public class DownloadsController : ControllerBase + private readonly IFileService _fileService = fileService; + private const string MimeType = "image/png"; + private const string FileName = "CM-Logo.png"; + + [HttpGet("images-byte")] + public IActionResult ReturnByteArray() + { + var image = _fileService.GetImageAsByteArray(); + + return File(image, MimeType, FileName); + } + + [HttpGet("images-stream")] + public IActionResult ReturnStream() { - private readonly IFileService _fileService; - private const string MimeType = "image/png"; - private const string FileName = "CM-Logo.png"; - - public DownloadsController(IFileService fileService) - { - _fileService = fileService; - } - - [HttpGet("images-byte")] - public IActionResult ReturnByteArray() - { - var image = _fileService.GetImageAsByteArray(); - - return File(image, MimeType, FileName); - } - - [HttpGet("images-stream")] - public IActionResult ReturnStream() - { - var image = _fileService.GetImageAsStream(); - - return File(image, MimeType, FileName); - } + var image = _fileService.GetImageAsStream(); + + return File(image, MimeType, FileName); + } + + [HttpGet("images-stream-range")] + public IActionResult ReturnStreamWithRanges() + { + var image = _fileService.GetImageAsStream(); + + return File(image, MimeType, FileName, enableRangeProcessing: true); } } diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Interface/IFileService.cs b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Interface/IFileService.cs index 415ed2717f..0a031e90aa 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Interface/IFileService.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Interface/IFileService.cs @@ -1,8 +1,7 @@ -namespace ReturnFileWebApi.Interface +namespace ReturnFileWebApi.Interface; + +public interface IFileService { - public interface IFileService - { - Stream GetImageAsStream(); - byte[] GetImageAsByteArray(); - } + Stream GetImageAsStream(); + byte[] GetImageAsByteArray(); } diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Program.cs b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Program.cs index 86a7645a50..7d46e9f715 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Program.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Program.cs @@ -6,9 +6,8 @@ // Add services to the container. builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); builder.Services.AddScoped(); var app = builder.Build(); @@ -16,8 +15,7 @@ // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); } app.UseHttpsRedirection(); @@ -26,4 +24,7 @@ app.MapControllers(); +app.MapGet("/minimal/images-stream", (IFileService fileService) => + Results.File(fileService.GetImageAsStream(), "image/png", "CM-Logo.png")); + app.Run(); diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Properties/launchSettings.json b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Properties/launchSettings.json index af15cc60b6..84fce76a15 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Properties/launchSettings.json +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Properties/launchSettings.json @@ -13,7 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "openapi/v1.json", "applicationUrl": "https://localhost:7248;http://localhost:5248", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -22,7 +22,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "openapi/v1.json", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/ReturnFileWebApi.csproj b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/ReturnFileWebApi.csproj index bdcf3d2beb..b9458cec16 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/ReturnFileWebApi.csproj +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/ReturnFileWebApi.csproj @@ -1,13 +1,13 @@ - net6.0 + net10.0 enable enable - + diff --git a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Services/FileService.cs b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Services/FileService.cs index 1b1212629c..67691e1f42 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Services/FileService.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Services/FileService.cs @@ -1,23 +1,21 @@ using ReturnFileWebApi.Assets; using ReturnFileWebApi.Interface; -using System.Buffers.Text; -namespace ReturnFileWebApi.Services +namespace ReturnFileWebApi.Services; + +public class FileService : IFileService { - public class FileService : IFileService + public Stream GetImageAsStream() { - public Stream GetImageAsStream() - { - var stream = new MemoryStream(Convert.FromBase64String(Image.Base64Image)); + var stream = new MemoryStream(Convert.FromBase64String(Image.Base64Image)); - return stream; - } + return stream; + } - public byte[] GetImageAsByteArray() - { - var bytes = Convert.FromBase64String(Image.Base64Image); + public byte[] GetImageAsByteArray() + { + var bytes = Convert.FromBase64String(Image.Base64Image); - return bytes; - } + return bytes; } } diff --git a/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.cs b/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.cs index 619733f7fa..e4f9c903d8 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.cs +++ b/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.cs @@ -1,51 +1,54 @@ -using Moq; +using Microsoft.AspNetCore.Mvc; +using NSubstitute; using ReturnFileWebApi.Assets; using ReturnFileWebApi.Controllers; using ReturnFileWebApi.Interface; -namespace Tests +namespace Tests; + +public class Tests { - public class Tests - { - private readonly Mock _fileServiceMock; + private readonly IFileService _fileService = Substitute.For(); - public Tests() - { - _fileServiceMock = new(); + public Tests() + { + _fileService.GetImageAsByteArray() + .Returns(Convert.FromBase64String(Image.Base64Image)); - SetupMocks(); - } + _fileService.GetImageAsStream() + .Returns(new MemoryStream(Convert.FromBase64String(Image.Base64Image))); + } - private void SetupMocks() - { - _fileServiceMock.Setup(x => x.GetImageAsByteArray()) - .Returns(Convert.FromBase64String(Image.Base64Image)) - .Verifiable(); + [Fact] + public void GivenAnImagesbyteRoute_WhenUsingByteArray_ThenReturnAFileToDownload() + { + var controller = new DownloadsController(_fileService); - _fileServiceMock.Setup(x => x.GetImageAsStream()) - .Returns(new MemoryStream(Convert.FromBase64String(Image.Base64Image))) - .Verifiable(); - } + var result = Assert.IsType(controller.ReturnByteArray()); - [Fact] - public void GivenAnImagesbyteRoute_WhenUsingByteArray_ThenReturnAFileToDownload() - { - var controller = new DownloadsController(_fileServiceMock.Object); + Assert.Equal("image/png", result.ContentType); + Assert.Equal("CM-Logo.png", result.FileDownloadName); + } - var image = controller.ReturnByteArray(); + [Fact] + public void GivenAnImagesStreamRoute_WhenUsingStream_ThenReturnAFileToDownload() + { + var controller = new DownloadsController(_fileService); - Assert.NotNull(image); - } + var result = Assert.IsType(controller.ReturnStream()); - [Fact] + Assert.Equal("image/png", result.ContentType); + Assert.Equal("CM-Logo.png", result.FileDownloadName); + } - public void GivenAnImagesStreamRoute_WhenUsingStream_ThenReturnAFileToDownload() - { - var controller = new DownloadsController(_fileServiceMock.Object); + [Fact] + public void GivenAnImagesStreamRangeRoute_WhenUsingStream_ThenEnableRangeProcessing() + { + var controller = new DownloadsController(_fileService); - var image = controller.ReturnStream(); + var result = Assert.IsType(controller.ReturnStreamWithRanges()); - Assert.NotNull(image); - } + Assert.True(result.EnableRangeProcessing); + Assert.Equal("image/png", result.ContentType); } -} \ No newline at end of file +} diff --git a/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.csproj b/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.csproj index c74f06cbd4..711cde5670 100644 --- a/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.csproj +++ b/aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 enable enable @@ -9,14 +9,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all