I am developing web API for work with files. I am trying to download file from azure blob storage and return it as response. For development API I using FastEndpoints
.
On Stack Overflow I found out that the best option is to use FileStreamResult
, but I am getting error:
System.NotSupportedException: Deserialization of types without a parameterless constructor
How can I resolve it and why I am facing it?
Endpoint
public class Endpoint : Endpoint<DownloadFileRequest, FileStreamResult>
{
private readonly DatabaseContext _context;
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
public Endpoint(DatabaseContext context, ILogger<Endpoint> logger, IConfiguration configuration)
{
_context = context;
_logger = logger;
_configuration = configuration;
}
public override void Configure()
{
Get("/file/{id}/download");
Roles(Role.Administrator, Role.ProjectManager, Role.GraphicDesigner, Role.Accountant);
Description(b => b.WithName("DownloadFile"));
}
public override async Task HandleAsync(DownloadFileRequest r, CancellationToken c)
{
var file = await Data.GetTriadaFileAsync(_context, r.Id, c); // gets file to
// download
if (file == null)
{
await SendNotFoundAsync(c);
return;
}
var result = await Data.DownloadBlobAsync(file.AzureName, r, c, _configuration);
if (result != null)
{
Response.FileStream = result.FileStream;
}
}
}
Data
public static class Data
{
public static async Task<TriadaFile?> GetTriadaFileAsync(DatabaseContext context, Guid id, CancellationToken c)
{
return await context.Files
.FirstOrDefaultAsync(x => x.Id == id, c);
}
public static async Task<FileStreamResult?> DownloadBlobAsync(string blobFileName, DownloadFileRequest r, CancellationToken c, IConfiguration _configuration)
{
string connectionString = _configuration.GetSection("Azure").GetValue<string>("BlobConnectionString")!;
string containerName = _configuration.GetSection("Azure").GetValue<string>("BlobContainerName")!;
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
BlobClient blobClient = containerClient.GetBlobClient(blobFileName);
using (var stream = new MemoryStream())
{
await blobClient.DownloadToAsync(stream);
stream.Position = 0;
var contentType = (await blobClient.GetPropertiesAsync()).Value.ContentType;
var fileStreamResult = new FileStreamResult(stream, "application/octet-stream");
fileStreamResult.FileDownloadName = blobFileName;
return fileStreamResult;
}
}
}
Models
public class DownloadFileRequest
{
public Guid Id{ get; set; }
}
public class Validator : Validator<DownloadFileRequest>
{
public Validator()
{
RuleFor(x => x.Id)
.NotEmpty();
}
}
public class DownloadFileResponse
{
}
FileStreamResult
is a relic from MVC framework days. we do things a bit diferrently now. your requirement can be simplified like this: