Minimal APIs - Exportando dados para arquivos do Excel


  Neste artigo mostrar como exportar dados para arquivos do Excel usando as minimal APIs.

Como desenvolvedor, você pode precisar trabalhar com fontes de dados baseadas em arquivos, e exportar dados para arquivos Excel é um requisito comum em muitos aplicativos.

Neste artigo, vamos explorar como exportar dados para arquivos Excel em um aplicativo ASP.NET Core usando a biblioteca MiniExcel.

Vamos criar um novo projeto de ASP.NET Core Web API e incluir o pacote nuget MiniExcel. Em seguida, criaremos uma classe modelo para os dados que queremos exportar e configuraremos nossa API para exportar os dados para um arquivo Excel.

Criando o projeto

Abra o VS 2022 Preview e selecione o template ASP.NET Core Web API informando a seguir o nome ApiExcel.

Neste projeto não vamos usar Controllers e assim vamos criar uma minimal API no ambiente do .NET 8.

Com o projeto criar inclua o pacote nuget MiniExcel no projeto :

<PackageReference Include="MiniExcel" Version="1.31.2" />

No projeto criado vamos criar as pastas Models e Api.

Na pasta Models vamos criar a classe Tarefa com o seguinte código:

using MiniExcelLibs.Attributes;
namespace ApiExcel.Models;
public class Tarefa
{
    [ExcelColumn(Name = "Id", Index = 0, Width = 40)]
    public Guid Id { get; set; }
    [ExcelColumn(Name = "Title", Index = 1, Width = 100)]
    public string Title { get; set; } = default!;
    [ExcelColumn(Ignore = true)]
    public bool IsComplete { get; set; }
    public static IEnumerable<Tarefa> GetList()
    {
        return new List<Tarefa>()
        {
          new Tarefa{
            Id = Guid.NewGuid(),
            Title = "Escrever um artigo sobre o .NET 8.",
            IsComplete = true
          },
          new Tarefa{
            Id = Guid.NewGuid(),
            Title = "Revisar o código do projeto",
            IsComplete = true
          },
          new Tarefa{
            Id = Guid.NewGuid(),
            Title = "Ler o livro 'Implementing Domain Driven Design' 
                      (https://abp.io/books/implementing-domain-driven-design)",
            IsComplete = true
          },
          new Tarefa{
            Id = Guid.NewGuid(),
            Title = "Descansar no sábado",
            IsComplete = false
          },
        };
    }
}

Esta classe contém os campos que correspondem ao header do arquivo Excel.

Usamos ExcelColumnAttribute para especificar o nome da coluna, índice, etc., isso não é obrigatório, mas é um dos atributos que você pode usar quando quiser personalizar sua tabela do Excel ao usar o MiniExcel.

O método GetList retorna a lista de tarefas para simplificar o exemplo e não ter que usar um banco de dados.

Agora na pasta Api vamos criar o método de extensão MapTarefas definindo a classe estática ApiTarefa :

using ApiExcel.Models;
using Microsoft.AspNetCore.Http.HttpResults;
using MiniExcelLibs;
namespace ApiExcel.Api;
public static class ApiTarefa
{
    public static RouteGroupBuilder MapTarefas(this IEndpointRouteBuilder routes)
    {
        var group = routes.MapGroup("/tarefas");
        group.WithTags("Tarefas");
        group.MapGet("/", async () => await Task.FromResult(Tarefa.GetList()));
        group.MapGet("/exportar", async Task<Results<FileStreamHttpResult, NotFound>> 
            (bool isComplete, CancellationToken token) =>
        {
            var filteredList = Tarefa.GetList().Where(x => 
                                      x.IsComplete == isComplete).ToList();
            if (filteredList.Count == 0)
            {
                return TypedResults.NotFound();
            }
            var memoryStream = new MemoryStream();
            await memoryStream.SaveAsAsync(filteredList, cancellationToken: token);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return TypedResults.File(memoryStream, 
                                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                                     "tarefas.xlsx");
        });
        return group;
    }
}

Este código define uma classe estática ApiTarefa com um único método MapTarefas que estende a interface IEndpointRouteBuilder adicionando novos endpoints para tarefas.

O método MapTarefas gera um novo grupo de rotas com o caminho base /tarefas e a tag Tarefas. O grupo tem dois endpoints definidos:

  1. O primeiro endpoint mapeia uma solicitação HTTP GET para o caminho base e retorna uma lista de tarefas;
  2. O segundo endpoint mapeia uma solicitação HTTP GET para /exportar e retorna um fluxo de arquivo de um arquivo Excel contendo uma lista filtrada de tarefas.
    O filtro é baseado no parâmetro booleano isComplete passado na URL da solicitação. Se a lista filtrada estiver vazia, ela retornará um resultado HTTP NotFound. Caso contrário, ele salva a lista filtrada em um fluxo de memória usando a biblioteca MiniExcel e retorna o fluxo de memória como um arquivo

Usamos TypedResults como valor de retorno em vez de Results porque TypedResults é o tipo de implementação que fornece automaticamente os metadados do tipo de resposta para OpenAPI descrever o endpoint.

Para concluir vamos definir o seguinte código na classe Program:

using ApiExcel.Api;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.Map("/", () => Results.Redirect("/swagger"));
// Configura a API
app.MapTarefas(); 
app.Run(); 

Executando o projeto teremos o seguinte resultado na interface do Swagger:

Acionando o endpoint GET /tarefas vamos obter:

Acionando o endpoint /tarefas/exportar teremos o seguinte resultado filtrando as tarefas para exibir apenas as tarefas completadas:

Resultado:

Aqui podemos clicar no link Download file e baixar o arquivo excel gerado (tarefas.xlsx) com os dados.

Pegue o projeto aqui :   ApiExcel.zip

"A minha alma espera somente em Deus; dele vem a minha salvação.
Só ele é a minha rocha e a minha salvação; é a minha defesa; não serei grandemente abalado"
Salmos 62:1,2

Referências:


José Carlos Macoratti