Skip to content

Commit 9f1e57c

Browse files
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.
1 parent 09f1dfa commit 9f1e57c

9 files changed

Lines changed: 104 additions & 103 deletions

File tree

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
namespace ReturnFileWebApi.Assets
1+
namespace ReturnFileWebApi.Assets;
2+
3+
public static class Image
24
{
3-
public static class Image
4-
{
5-
public const string Base64Image = "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";
6-
}
5+
public const string Base64Image = "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";
76
}
Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
1-
using Microsoft.AspNetCore.Http;
2-
using Microsoft.AspNetCore.Mvc;
1+
using Microsoft.AspNetCore.Mvc;
32
using ReturnFileWebApi.Interface;
43

5-
namespace ReturnFileWebApi.Controllers
4+
namespace ReturnFileWebApi.Controllers;
5+
6+
[Route("api/[controller]")]
7+
[ApiController]
8+
public class DownloadsController(IFileService fileService) : ControllerBase
69
{
7-
[Route("api/[controller]")]
8-
[ApiController]
9-
public class DownloadsController : ControllerBase
10+
private readonly IFileService _fileService = fileService;
11+
private const string MimeType = "image/png";
12+
private const string FileName = "CM-Logo.png";
13+
14+
[HttpGet("images-byte")]
15+
public IActionResult ReturnByteArray()
16+
{
17+
var image = _fileService.GetImageAsByteArray();
18+
19+
return File(image, MimeType, FileName);
20+
}
21+
22+
[HttpGet("images-stream")]
23+
public IActionResult ReturnStream()
1024
{
11-
private readonly IFileService _fileService;
12-
private const string MimeType = "image/png";
13-
private const string FileName = "CM-Logo.png";
14-
15-
public DownloadsController(IFileService fileService)
16-
{
17-
_fileService = fileService;
18-
}
19-
20-
[HttpGet("images-byte")]
21-
public IActionResult ReturnByteArray()
22-
{
23-
var image = _fileService.GetImageAsByteArray();
24-
25-
return File(image, MimeType, FileName);
26-
}
27-
28-
[HttpGet("images-stream")]
29-
public IActionResult ReturnStream()
30-
{
31-
var image = _fileService.GetImageAsStream();
32-
33-
return File(image, MimeType, FileName);
34-
}
25+
var image = _fileService.GetImageAsStream();
26+
27+
return File(image, MimeType, FileName);
28+
}
29+
30+
[HttpGet("images-stream-range")]
31+
public IActionResult ReturnStreamWithRanges()
32+
{
33+
var image = _fileService.GetImageAsStream();
34+
35+
return File(image, MimeType, FileName, enableRangeProcessing: true);
3536
}
3637
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
namespace ReturnFileWebApi.Interface
1+
namespace ReturnFileWebApi.Interface;
2+
3+
public interface IFileService
24
{
3-
public interface IFileService
4-
{
5-
Stream GetImageAsStream();
6-
byte[] GetImageAsByteArray();
7-
}
5+
Stream GetImageAsStream();
6+
byte[] GetImageAsByteArray();
87
}

‎aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Program.cs‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@
66
// Add services to the container.
77

88
builder.Services.AddControllers();
9-
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
10-
builder.Services.AddEndpointsApiExplorer();
11-
builder.Services.AddSwaggerGen();
9+
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
10+
builder.Services.AddOpenApi();
1211
builder.Services.AddScoped<IFileService, FileService>();
1312

1413
var app = builder.Build();
1514

1615
// Configure the HTTP request pipeline.
1716
if (app.Environment.IsDevelopment())
1817
{
19-
app.UseSwagger();
20-
app.UseSwaggerUI();
18+
app.MapOpenApi();
2119
}
2220

2321
app.UseHttpsRedirection();
@@ -26,4 +24,7 @@
2624

2725
app.MapControllers();
2826

27+
app.MapGet("/minimal/images-stream", (IFileService fileService) =>
28+
Results.File(fileService.GetImageAsStream(), "image/png", "CM-Logo.png"));
29+
2930
app.Run();

‎aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Properties/launchSettings.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"commandName": "Project",
1414
"dotnetRunMessages": true,
1515
"launchBrowser": true,
16-
"launchUrl": "swagger",
16+
"launchUrl": "openapi/v1.json",
1717
"applicationUrl": "https://localhost:7248;http://localhost:5248",
1818
"environmentVariables": {
1919
"ASPNETCORE_ENVIRONMENT": "Development"
@@ -22,7 +22,7 @@
2222
"IIS Express": {
2323
"commandName": "IISExpress",
2424
"launchBrowser": true,
25-
"launchUrl": "swagger",
25+
"launchUrl": "openapi/v1.json",
2626
"environmentVariables": {
2727
"ASPNETCORE_ENVIRONMENT": "Development"
2828
}

‎aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/ReturnFileWebApi.csproj‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
10+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
1111
</ItemGroup>
1212

1313
<ItemGroup>
Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
using ReturnFileWebApi.Assets;
22
using ReturnFileWebApi.Interface;
3-
using System.Buffers.Text;
43

5-
namespace ReturnFileWebApi.Services
4+
namespace ReturnFileWebApi.Services;
5+
6+
public class FileService : IFileService
67
{
7-
public class FileService : IFileService
8+
public Stream GetImageAsStream()
89
{
9-
public Stream GetImageAsStream()
10-
{
11-
var stream = new MemoryStream(Convert.FromBase64String(Image.Base64Image));
10+
var stream = new MemoryStream(Convert.FromBase64String(Image.Base64Image));
1211

13-
return stream;
14-
}
12+
return stream;
13+
}
1514

16-
public byte[] GetImageAsByteArray()
17-
{
18-
var bytes = Convert.FromBase64String(Image.Base64Image);
15+
public byte[] GetImageAsByteArray()
16+
{
17+
var bytes = Convert.FromBase64String(Image.Base64Image);
1918

20-
return bytes;
21-
}
19+
return bytes;
2220
}
2321
}
Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,54 @@
1-
using Moq;
1+
using Microsoft.AspNetCore.Mvc;
2+
using NSubstitute;
23
using ReturnFileWebApi.Assets;
34
using ReturnFileWebApi.Controllers;
45
using ReturnFileWebApi.Interface;
56

6-
namespace Tests
7+
namespace Tests;
8+
9+
public class Tests
710
{
8-
public class Tests
9-
{
10-
private readonly Mock<IFileService> _fileServiceMock;
11+
private readonly IFileService _fileService = Substitute.For<IFileService>();
1112

12-
public Tests()
13-
{
14-
_fileServiceMock = new();
13+
public Tests()
14+
{
15+
_fileService.GetImageAsByteArray()
16+
.Returns(Convert.FromBase64String(Image.Base64Image));
1517

16-
SetupMocks();
17-
}
18+
_fileService.GetImageAsStream()
19+
.Returns(new MemoryStream(Convert.FromBase64String(Image.Base64Image)));
20+
}
1821

19-
private void SetupMocks()
20-
{
21-
_fileServiceMock.Setup(x => x.GetImageAsByteArray())
22-
.Returns(Convert.FromBase64String(Image.Base64Image))
23-
.Verifiable();
22+
[Fact]
23+
public void GivenAnImagesbyteRoute_WhenUsingByteArray_ThenReturnAFileToDownload()
24+
{
25+
var controller = new DownloadsController(_fileService);
2426

25-
_fileServiceMock.Setup(x => x.GetImageAsStream())
26-
.Returns(new MemoryStream(Convert.FromBase64String(Image.Base64Image)))
27-
.Verifiable();
28-
}
27+
var result = Assert.IsType<FileContentResult>(controller.ReturnByteArray());
2928

30-
[Fact]
31-
public void GivenAnImagesbyteRoute_WhenUsingByteArray_ThenReturnAFileToDownload()
32-
{
33-
var controller = new DownloadsController(_fileServiceMock.Object);
29+
Assert.Equal("image/png", result.ContentType);
30+
Assert.Equal("CM-Logo.png", result.FileDownloadName);
31+
}
3432

35-
var image = controller.ReturnByteArray();
33+
[Fact]
34+
public void GivenAnImagesStreamRoute_WhenUsingStream_ThenReturnAFileToDownload()
35+
{
36+
var controller = new DownloadsController(_fileService);
3637

37-
Assert.NotNull(image);
38-
}
38+
var result = Assert.IsType<FileStreamResult>(controller.ReturnStream());
3939

40-
[Fact]
40+
Assert.Equal("image/png", result.ContentType);
41+
Assert.Equal("CM-Logo.png", result.FileDownloadName);
42+
}
4143

42-
public void GivenAnImagesStreamRoute_WhenUsingStream_ThenReturnAFileToDownload()
43-
{
44-
var controller = new DownloadsController(_fileServiceMock.Object);
44+
[Fact]
45+
public void GivenAnImagesStreamRangeRoute_WhenUsingStream_ThenEnableRangeProcessing()
46+
{
47+
var controller = new DownloadsController(_fileService);
4548

46-
var image = controller.ReturnStream();
49+
var result = Assert.IsType<FileStreamResult>(controller.ReturnStreamWithRanges());
4750

48-
Assert.NotNull(image);
49-
}
51+
Assert.True(result.EnableRangeProcessing);
52+
Assert.Equal("image/png", result.ContentType);
5053
}
51-
}
54+
}

‎aspnetcore-webapi/ReturnFileWebApi/Tests/Tests.csproj‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
13-
<PackageReference Include="Moq" Version="4.18.2" />
14-
<PackageReference Include="xunit" Version="2.4.1" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
13+
<PackageReference Include="NSubstitute" Version="6.2.0" />
14+
<PackageReference Include="xunit" Version="2.9.3" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>
19-
<PackageReference Include="coverlet.collector" Version="3.1.2">
19+
<PackageReference Include="coverlet.collector" Version="10.1.0">
2020
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2121
<PrivateAssets>all</PrivateAssets>
2222
</PackageReference>

0 commit comments

Comments
 (0)