Minimal APIs - Vinculação a formulários


  Neste artigo vou apresentar alguns dos novos recursos das minimal APIs no .NET 8.

Vamos iniciar apresentando os principais conceitos sobre as minimal APIs para quem esta começando agora.

Introdução

As Minimal APIs referem-se a uma abordagem mais simplificada para a criação de APIs em comparação com estruturas mais pesadas.

As minimal APIs são constituidas dos seguintes recursos básicos:

  1. WebApplication e WebApplicationBuilder
  2. Route Handlers

Elas são baseadas na classe WebApplication e usam a abordagem funcional para a definição de rotas e manipulação de solicitações. A estrutura básica pode ser entendida assim:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

Aqui, você cria uma instância de WebApplication usando o WebApplication.CreateBuilder e, em seguida, constrói a aplicação.

Você define os endpoints diretamente no método MapGet, MapPost, MapPut, MapDelete, etc., sem a necessidade de um controlador separado.


  app.MapGet("/", () => "Bem-Vindo!");
 

Você pode injetar dependências diretamente nos métodos de manipulação de solicitações.

Por exemplo, se você precisa de um serviço, pode simplesmente incluí-lo como parâmetro no método, a seguir registre o serviço no container DI e defina o endpoint com a rota e injete o serviço no endpoint:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton(new SaudacaoService());

var app = builder.Build();

app.MapGet("/", () => "Bem-Vindo!")

app.MapGet("/saudacao/{nome}", (string nome,SaudacaoService saudacao) =>
           Results.Ok(saudacao.Saudacao(nome)));

app.UseHttpsRedirection();
app.Run();

public class SaudacaoService
{
   public string Saudacao(string nome) => $"Bem-Vindo {nome}";
}

Se desejar incluir os recursos do Swagger basta incluir no projeto o pacote nuget :  Swashbuckle.AspNetCore

E alterar o código conforme abaixo:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen()
;

builder.Services.AddSingleton(new SaudacaoService());

var app = builder.Build();

app.MapGet("/", () => "Bem-Vindo!")

app.MapGet("/saudacao/{nome}", (string nome,SaudacaoService saudacao) =>
           Results.Ok(saudacao.Saudacao(nome)));

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();
app.Run();

public class SaudacaoService
{
   public string Saudacao(string nome) => $"Bem-Vindo {nome}";
}

Um recurso recém incorporado às minimal APIs é a capacidade de agrupar logicamente as rotas para um prefixo de caminho base usando RouteGroupBuilder.

Para o exemplo acima vamos criar um novo grupo de rotas definido como 'teste':

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton(new SaudacaoService());

var app = builder.Build();

var teste = app.MapGroup("teste");

teste.MapGet("/", () => "Bem-Vindo");

teste.MapGet("/saudacao/{nome}", (string nome,SaudacaoService saudacao) =>
            Results.Ok(saudacao.Saudacao(nome)));

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();
app.Run();

public class SaudacaoService
{
  public string Saudacao(string nome) => $"Bem-Vindo {nome}";
}

O que vai produzir o seguinte resultado na interface do Swagger:

Apresentando a vinculação a formulários

Nas Minimal APIs já podíamos usar a interface IFormFile da mesma maneira que em aplicações mais tradicionais. Esta interface é usada para representar arquivos enviados por meio de um request HTTP multipart/form-data.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/upload", (IFormFile file) =>
{
    // Verificar se um arquivo foi realmente enviado
    if (file != null && file.Length > 0)
    {
        // Lógica para processar o arquivo aqui
        // Por exemplo, você pode salvá-lo em disco, 
        // processá-lo, etc.
        // file.CopyTo(...);
        // Ou usar um serviço para manipular o arquivo
        // fileService.ProcessFile(file);
        
        return $"Arquivo {file.FileName} recebido com sucesso!";
    }
    return "Nenhum arquivo enviado.";
});

app.Run();

A partir do .NET 8.0 podemos vincular formulários usando o atributo [FromForm]

Com o novo atributo [FromForm], os dados do formulário podem ser vinculados diretamente à API sem a necessidade de serialização. Isso simplifica o código e melhora o desempenho. Para usar o atributo [FromForm], basta adicioná-lo a um parâmetro de um manipulador de endpoint.

O framework irá então vincular o valor do campo de formulário com o mesmo nome ao parâmetro.


app.MapPost("/formulario1", ([FromForm] string descricao) =>
{
   //código
});
 

Se quiser vincular vários campos de formulário, você pode listá-los como parâmetros extras marcados com o atributo [FromForm]:


app.MapPost("/formulario2", ([FromForm] string nome, [FromForm] bool ativo,
                             [FromForm] DateTime nascimento)
                             => Results.Ok(new { nome, ativo, nascimento }));

 

Para evitar ter que usar uma longa lista de parâmetros temos também o suporte a objetos complexos :

app.MapPost("/matricula", ([FromForm] Aluno aluno)
                          => Results.Ok(aluno));
...
app.Run();

public class Aluno
{
  public string? Nome { get; set; }
  public bool Ativo{ get; set; }
  public DateTime Nascimento { get; set; }
}

Importante destacar que ao usar este recurso existem limitações e aspectos relacionados a segurança dos tokens anti-falsificação. (Antiforgery)

Antiforgery

O outro recurso disponível para as minimal APIs é o suporte aos tokens antiforgery.

Podemos chamar o método AddAntiforgery para registrar os serviços antifalsificação e o WebApplicationBuilder adicionará automaticamente o middleware antifalsificação ao pipeline:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton(new SaudacaoService());

builder.Services.AddAntiforgery();

var app = builder.Build();
...
App.Run();

Exemplo de uso de tokens antiforgery, onde a interface IAntiforgery esta sendo usada para validar o request e ao final estamos desabilitando a validação :

app.MapPost("/aluno", async Task<IResult> ([FromForm] Aluno aluno,

HttpContext context, IAntiforgery antiforgery) =>
{
  try
  {
    await antiforgery.ValidateRequestAsync(context);
    return Results.Ok(aluno);
  }
  catch (AntiforgeryValidationException)
  {
    return TypedResults.BadRequest("token anti-forgery inválido");
   }
}).DisableAntiforgery();

Temos assim estes novos recursos presentes agora nas minimal APIs .

E estamos conversados...

"Porque virá tempo em que não suportarão a sã doutrina; mas, tendo coceira nos ouvidos, amontoarão para si doutores conforme as suas próprias concupiscências;"
2 Timóteo 4:3

Referências:


José Carlos Macoratti