Middlewares no ASP.Net Core

Podemos entender um middleware como um componente (nesse caso uma classe) adicionada no pipeline de execução do Asp.Net entre o Request e o Response. Ao fazer uma requisição, é inciado um fluxo que executa cada um dos middlewares que são encontrados no caminho até o momento da volta ou até encontrar uma finalização por um algum middleware. A figura abaixo ilustra bem esse processo de execução dos middlewares.

Imagem 1 – Fluxo de execução dos middlewares no pipeline.

Um middleware geralmente tem um propósito bem definido, mas vale lembrar que ele pode não executar nenhuma tarefa. No Asp.Net Core temos vários middlewares responsáveis pelas mais variadas tarefas como tratamento de erros, autenticação, manipulação de arquivos estáticos, política de cookies etc.

A ordem

Ao adicionar um middleware, é de extrema importância que prestemos atenção na ordem em que os configuramos. A execução dos middlewares é feita exatamente na ordem que os adicionamos e o retorno segue a mesma sequência na ordem inversa. Essa ordem pode afetar diretamente a nossa aplicação no que tange aspectos como performance, segurança e funcionalidades.

Configurando um middleware

Na classe Startup.cs há um método chamado Configure. Esse método permite a configuração (adição no pipeline) dos middlewares que iremos utilizar. Essa configuração se dá através da instância de IApplicationBuilder.

public class Startup
{
    public Startup()
    {
    } 
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {               
        app.Run(async (context) =>
        {              
            await context.Response.WriteAsync("My first middleware");
              
        });
    }
}

As linhas destacadas, representam nosso middleware, nesse caso um middleware anônimo. Note que temos uma instância de IApplicationBuilder chamada app a qual nos permite a adição dos middlewares.

Entendendo Run, Use e Map

Podemos utilizar qualquer um destes três métodos de extensão na utilização dos middlewares. Por serem convenções, é importante manter o padrão quando criamos nossos middlewares.

Run => adiciona um middleware terminal ao pipeline do Asp.Net Core. Isso significa que após sua execução, ocorrerá uma interrupção do fluxo no pipeline causando um curto-circuito e nenhum outro middleware será executado.

Use => adiciona um middleware in-line no pipeline – rimou rsrs-. Isso significa que após a execução desse middleware, ele fará uma chamada para o próximo.

Map => cria uma ramificação do pipeline com base no caminho informado. O exemplo abaixo do Microsoft docs, explica bem esse cenário.

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

Os resultados do código acima seriam:

SolicitaçãoResposta
localhost:1234Saudação do delegate diferente de Map.
localhost:1234/map1Teste de Map 1
localhost:1234/map2Teste de Map 2
localhost:1234/map3Saudação do delegate diferente de Map.

Criando um middleware

Nosso middleware não será um middleware anônimo, faremos o uso de uma classe reutilizável que junto com a classe do middleware, forma o que chamamos de componentes do middleware.

Uma classe middleware deverá atender alguns requisitos:

1 – possuir um construtor público que aceite um argumento do tipo RequestDelegate. Esse atributo – junto com o construtor – fará a manipulação de requisições HTTP e irá chamar o próximo middleware do pipeline;

2 – um método público chamado Invoke ou InvokeAsync onde o primeiro argumento deverá ser do tipo HttpContext e este método deverá retornar uma Task.

Sabendo destes requisitos, vamos criar a nossa classe da seguinte maneira:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

public class MeuPrimeiroMiddleware
{
   
   private readonly RequestDelegate _next;

   public MeuPrimeiroMiddleware(RequestDelegate next)
   {
      _next = next; 
   }

   public async Task InvokeAsync(HttpContext context)
   {
      Console.WriteLine("\n\r----- Iniciando meu middleware -----\n\r");
      await _next(context);
      Console.WriteLine("\n\r----- Finalizando meu middleware -----\n\r");
   }
}

A classe acima já possui todos os critérios para o nosso primeiro middleware. Na verdade essa é a estrutura básica para um middleware, a partir daí poderá ser melhorada conforme seja necessário, mas o “esqueleto” é esse.

O método InvokeAsync irá executar uma tarefa – nesse caso escrever a mensagem “Iniciando meu middleware” – e fazer a chamada para o próximo middleware (await _next(context)). No retorno, irá finalizar o middleware com a mensagem ” Finalizando meu middleware“.

O próximo passo é criar a classe de reutilização que permitirá através de um método de extensão, a configuração do nosso middleware no pipeline do Asp.Net Core.

public static class MeuPrimeiroMiddlewareExtension
{
    public static IApplicationBuilder UseMeuPrimeiroMiddleware(this IApplicationBuilder builder){
        return builder.UseMiddleware<MeuPrimeiroMiddleware>();
    }
}

A classe acima, é uma classe estática bem simples e com um único método estático que adiciona através do método UseMiddleware um tipo, – nesse caso a nossa classe MeuPrimeiroMiddleware – ao pipeline de execução.

Agora no método Configure, basta que adicionemos nosso middleware conforme o código abaixo:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
           
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            
            app.UseCookiePolicy();
            app.UseMeuPrimeiroMiddleware();          

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
        }

Na linha em destaque (linha 19) temos nosso middleware já adicionado e o resultado da execução podemos conferir na imagem abaixo.

Imagem 2 – Teste com o middleware.

Podemos verificar que após o início do nosso middleware, onde é escrito o texto “Iniciando meu middleware” chamamos o próximo middleware, que também faz uma chamada para o próximo e assim continua até o último, onde inicia o retorno na ordem inversa.

Fonte: Microsoft Docs

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s