Escolha uma Página
(Last Updated On: 27/06/2018)

ASP.NET CORE e MVC são diferentes…

O ASP.NET Core continua a ser a principal plataforma subjacente para a construção de aplicativos da Web no .NET Core. Já o MVC ainda é uma estrutura web opcional, que pode ser conectada ao pipeline ASP.NET Core.

É basicamente uma biblioteca NuGet que fica no topo do ASP.NET Core e oferece alguns recursos adicionais para o padrão de design Model-View-Controller.

O que isso significa em termos de tratamento de erros é que qualquer capacidade de gerenciamento de exceção oferecida pela MVC ficará limitada ao MVC. Isso se tornará muito mais evidente quando analisarmos a arquitetura ASP.NET Core.

O middleware do ASP.NET CORE

O ASP.NET Core é completamente modular e o pipeline de solicitações é definida principalmente pelo “middleware” instalado em uma aplicação.

Para entender melhor vamos criar uma nova aplicação MVC e analisar o método

"void Configure (...)"

que fica sempre dentro do arquivo Startup.cs.

public void Configure(
    IApplicationBuilder app,
    IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentity();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Porque esse é um código muito extenso para um aplicativo web simples, vou cortá-lo para os principais pontos de interesse:

app.UseExceptionHandler("/Home/Error");
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes => ...);

O que você pode ver aqui é essencialmente auto-explicativo, mas há algumas coisas importantes para entender nesse código. As chamadas de método app.Use … () (extensão-) estão ativando vários middlewares registrando-os com o objeto IApplicationBuilder. Cada middleware será responsável por chamar o próximo middleware no pipeline de solicitações. É por isso que a ordem de chamada dos métodos do app.Use … () é importante!

Por exemplo, este é um esqueleto do Static File Middleware:

public StaticFileMiddleware(RequestDelegate next, ...)
{
    // Alguma coisa antes

    _next = next;

    // Alguma coisa deposi}

public Task Invoke(HttpContext context)
{
    // Um monte de código para ver se esse middleware pode
    // servir um arquivo estático que corresponde a uma solicitação HTTP ...

    // Caso contrário, o código chegará a esta linha:/
    return _next(context);
}

Foi cortado algum ruído para destacar o uso da variável RequestDelegate.

Como você pode ver, cada middleware deve aceitar um objeto RequestDelegate no construtor. E cada middleware deve implementar um método de tipo Task Invoke (no contexto HttpContext).

O RequestDelegate é, como seu nome sugere, um delegador que representa o próximo middleware no ciclo de vida da aplicação. ASP.NET Core tem a responsabilidade de invocá-lo, a partir do próprio middleware atual. Por exemplo, se o StaticFileMiddleware não conseguir encontrar um arquivo estático que corresponda à solicitação HTTP recebida, então ele invocará o próximo middleware chamando return _next (context); no fim. Por outro lado, se conseguiu encontrar o arquivo estático solicitado, ele o devolverá ao cliente e nunca mais invocará o próximo ou qualquer middleware subseqüente.

pipeline request - delegate core mvc

É por isso que a ordem do método app.Use … () é importante. Quando você pensa sobre isso, o padrão subjacente de qualquer requisição HTTP pode ser visto em camadas, como se fosse  uma “cebola”:

cebola core mvc de uma requisição HTTP

Uma requisição HTTP viajará do middleware de nível superior até o último middleware, a menos que um middleware no meio possa satisfazer a solicitação e retornar uma resposta HTTP mais cedo para o cliente.

Em contraste, uma exceção não tratada viajaria de baixo para cima. Começando no middleware onde ele foi parado, ela voltaria até o topo do middleware esperando por algo que possa pegá-lo. Em teoria, um middleware também poderia tentar fazer alterações na resposta depois de ter invocado o próximo middleware, mas isso normalmente não é o caso. o que não é aconselhável. Porque isso poderia resultar em uma exceção se o outro middleware já estivesse trabalhando na resposta à requisição HTTP.

O tratamento de erros deve estar no primeiro middleware

Com isso em mente, fica claro que para capturar qualquer exceção não tratada, um erro,  seu gerenciamento  deve ser o primeiro no pipeline do middleware.  Aí, então, se pode garantir uma captura final – se nada mais captar a exceção antes.

Como o MVC normalmente é registrado no final do pipeline do middleware, também é claro que os recursos de gerenciamento de exceção (como os infames ExceptionFilters) dentro do MVC não poderão capturar todas as exceções.

Para mais informações sobre middleware, verifique a documentação oficial sobre middleware.

Manipuladores de Exceção Personalizados (Custom Exception Handlers)

Entendendo melhor o funcionamento do middleware e a necessidade de tratar as exceções. é possível que você crie o seu próprio manipulador de exceção global no ASP.NET Core.

Embora já existam alguns manipuladores de exceção úteis no pacote NuGet Microsoft.AspNetCore.Diagnostics disponível, ainda pode fazer sentido criar o seu próprio. Por exemplo, você pode querer ter um manipulador de exceção que registre exceções críticas no Sentry (que ajuda\ a saber quando os aplicativos brecam, enviando notificações por e-mail, sms, etc.)usando o Raven Client for .NET do Sentry ou pode querer implementar uma integração com uma ferramenta de rastreamento de erros e registrar um novo ticket para cada NullReferenceException que ocorra. Outra opção seria uma integração com o elmah.io (outro monitorador de sites).

Bons motivos para você ter seus próprios Manipuladores de Exceção

Há razões muitas boas pelas quais você  pode querer criar manipuladores de exceção adicionais. Pode ser muito útil ter vários manipuladores de exceções, registrados de uma só vez. Por exemplo, o primeiro manipulador de exceção registra um “ticket” em um sistema de rastreamento de “bugs” (falhas) e relança a exceção original. Em seguida, o próximo manipulador de exceção poderá registrar o erro no ELMAH e reiniciar novamente a exceção original. O manipulador de exceção final pode capturar a exceção e retornar uma página de erro amigável para o cliente.

Ao ter cada manipulador de exceção concentrando-se em uma única atividade, no seu conjunto eles se tornam automaticamente mais reutilizáveis em vários projetos. também é possível usar diferentes combinações de manipuladores em diferentes ambientes (por exemplo, em dev / staging / production).

Um bom exemplo de escrever seu próprio middleware de gerenciamento de exceção é o padrão ExceptionHandlerMiddleware no ASP.NET Core.

Um “gabarito” de um manipulador de exceção “padrão” seria mais ou menos assim:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace SuaAplicacao
{
    public sealed class CustomExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public CustomExceptionHandlerMiddleware(
            RequestDelegate next,
            ILoggerFactory loggerFactory)
        {
            _next = next;
            _logger = loggerFactory.
                    CreateLogger<CustomExceptionHandlerMiddleware>();
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                try
                {
                    // Faz alguma coisa
                    // Pode ser algo tão simples como uma chamada para logger.LogError

                    // se você não quer retomar a exceção original
                    // então chame o return:
                    // return;
                }
                catch (Exception ex2)
                {
                    _logger.LogError(
                        0, ex2, 
                        "Uma exeção ocorreu tentando " + 
                        "ao utilizar o manipulador de exceções.");
                }

                // Caso contrário o manipulador irá
                // retentar a exceção original
                throw;
            }
        }
    }
}

Adicionalmente ao RequestDelegate, o construtor também aceita um ILoggerFactory – que pode ser usado para instanciar um novo objeto do ILogger.

No método Task Invoke (HttpContext context), o manipulador de erros basicamente não faz nada além de chamar imediatamente o próximo middleware. Somente se uma exceção for lançada, ela entrará em ação, capturando-a no bloco de captura. O que você colocou no bloco de captura depende de você, mas seria uma boa prática para envolver qualquer código não trivial em um segundo bloco try-catch e padrão de volta ao log básico se todo o resto estiver desmoronando.

Se você tiver dúvidas ou contribuições, deixe-me um comentário abaixo.

Referências:  

  1. Error Handling in ASP.NET Core (Dusted Codes) e 
  2. Error Handling in ASP.NET Core (Microsoft)

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Você quer ter uma franquia de hospedagem com tudo para marketing digital e faturamento recorrente?

franquia builderall business