ASP.NET Core  -  Implementando IResult


  Neste artigo vamos abordar a implementação da interface IResult para respostas customizadas nas APIs mínimas.

No .NET 6 as implementações de IActionResult do MVC forma estendidads para oferecer suporte ao novo tipo IResult introduzido para as APIs mínimas.



Agora, foram removidas as dependências entre IActionResult e IResult e foi adicionada uma nova classe estática Results  para produzir respostas HTTP comuns.

Assim, agora na APIs mínimas podemos usar classes estáticas como Results para retornar resultados tipados nas suas rotas e métodos Action. Isso ajuda a simplificar o código e torná-lo mais legível.

A classe estática Results oferece diversos métodos estáticos para retornar resultados comuns, como Ok(), Created(), BadRequest(), NotFound(), entre outros. Esses métodos retornam objetos ActionResult que encapsulam o resultado da ação e fornecem informações adicionais, como o código de status HTTP e o conteúdo da resposta.

Aqui está um exemplo de como usar a classe estática Results :

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/api/minhaaction", () =>
{
  
var data = new { Message = "Olá, Mundo!" };
  
return Results.Ok(data);
});

app.Run();

No exemplo acima, estamos mapeando uma rota "/api/minhaaction" usando o método MapGet. Dentro do bloco de código, estamos criando um objeto anônimo data com uma propriedade Message. Em seguida, estamos retornando o resultado Ok usando Results.Ok(data), que encapsula o objeto data e define o código de status HTTP 200 (OK) para a resposta.

Além do método Ok(), você pode usar outros métodos estáticos da classe Results para retornar resultados com diferentes códigos de status HTTP, como Created(), BadRequest(), NotFound(), NoContent(), entre outros.

Essa abordagem ajuda a manter o código mais conciso e expressivo, pois você não precisa criar explicitamente objetos ActionResult para cada tipo de resultado que deseja retornar.

Desta forma em uma API mínima para retornar uma resposta customizada temos que implementar a biblioteca : Microsoft.AspNetCore.Http.IResult:

class CustomResult : IResult
{
  
public Task ExecuteAsync(HttpContext httpContext)
   {
    
throw new NotImplementedException();
   }
}

O método 'ExecuteAsync' é invocado automaticamente, o único parâmetro que ele terá é o 'HttpContext' onde podemos usar para anexar nosso tipo de resposta personalizada.

Criando um projeto ASP.NET Core

Vamos criar agora em um projeto ASP.NET Core uma API minima no VS Code usando o seguinte comando: dotnet new webapi -minimal -o ApiCustomResult

Será criada a pasta ApiCustomResult contendo os endpoints definidos na classe Program.

No Visual Studio podemos criar um projeto usando o template ASP.NET Core Web API e definindo as seguintes configurações:

Para poder criar uma resposta customizada para um endpoint da nossa API mínima vamos criar uma classe que implementa a interface IResult.

Vamos criar a pasta CustomAPIResponse e nesta pasta crie a classe CustomHTMLResult com o código abaixo que vai permitir gerar uma resposta personalizada no formato HTML :

using System.Net.Mime;
using System.Text;
namespace ApiCustomResult.CustomAPIResponse;
public class CustomtHTMLResult : IResult
{
    private readonly string _htmlContent;

    public CustomtHTMLResult(string htmlContent)
    {
        _htmlContent = htmlContent;
    }
    public async Task ExecuteAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Html;
        httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_htmlContent);
        await httpContext.Response.WriteAsync(_htmlContent);
    }
}

Este código define uma classe chamada CustomtHTMLResult, que implementa a interface IResult e   será responsável por gerar uma resposta HTTP personalizada no formato HTML em um aplicativo ASP.NET Core 7.0.

A classe possui um construtor que recebe uma string htmlContent, que representa o conteúdo HTML que será retornado na resposta.

O método ExecuteAsync é um método assíncrono que executa a lógica para gerar a resposta HTTP personalizada. Ele recebe um objeto HttpContext, que representa o contexto da solicitação HTTP atual.

Dentro do método ExecuteAsync, o código define o tipo de conteúdo da resposta HTTP como "text/html" usando httpContext.Response.ContentType, e, em seguida, define o tamanho do conteúdo da resposta usando httpContext.Response.ContentLength, calculado com base no número de bytes do conteúdo HTML.

Finalmente, o método usa await httpContext.Response.WriteAsync(_htmlContent) para escrever o conteúdo HTML na resposta HTTP.

Agora vamos criar na pasta CustomAPIResponse um método de extensão chamado HtmlResponse na classe CustomResultExtensions que vai usar a classe que criamos anteriormente para gerar o response no formato HTML.

namespace ApiCustomResult.CustomAPIResponse;

public static class CustomResultExtensions
{
  
public static IResult HtmlResponse(this IResultExtensions extensions, string html)
   {
      
return new CustomtHTMLResult(html);
   }
}

Agora podemos definir um endpoint na minimal API que foi criada no projeto e usar a nossa implementação para ter uma resposta personalizada.

Para isso, Inclua na classe Program o código abaixo:

app.MapGet("/html-custom-response", () => Results.Extensions.HtmlResponse(
   
@"
       <html>
         <head></head>
         <body>
           <h1>Resposta personalizada da API Mínima</h1>
          </body>
        </html>
"
));

Este código define uma rota "/html-custom-response" para lidar com solicitações HTTP GET.

A expressão app.MapGet("/html-custom-response", () => Results.Extensions.HtmlResponse(...)) mapeia a rota "/html-custom-response" e especifica que uma resposta personalizada em HTML será retornada quando essa rota for acessada por uma solicitação HTTP GET.

A parte relevante é Results.Extensions.HtmlResponse(...), que chama o método de extensão HtmlResponse da classe Results.Extensions. Esse método cria uma instância da classe CustomtHTMLResult (como mencionado anteriormente) e passa o conteúdo HTML fornecido como parâmetro.

No exemplo fornecido, o conteúdo HTML é definido como uma string multilinha entre aspas triplas. O HTML em si consiste em um documento básico com um título "Resposta personalizada da API Mínima" dentro de uma tag h1.

Executando o projeto teremos os endpoints exibidos na interface do Swagger:

Executando o endpoint no navegador poderemos ver a resposta no formato HTML:

Pegue o projeto aqui: ApiCustomResult.zip

E estamos conversados ...

"Porque Deus, que disse que das trevas resplandecesse a luz, é quem resplandeceu em nossos corações, para iluminação do conhecimento da glória de Deus, na face de Jesus Cristo"
2 Coríntios 4:6

Referências:


José Carlos Macoratti