O erro do aplicativo não é acionado quando o cliente espelha = "Ativado"


124

Eu tenho um código no evento global.asaxdo arquivo Application_Errorque é executado quando ocorre um erro e envia por e-mail os detalhes do erro para mim.

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

Isso funciona bem quando eu estou executando no Visual Studio; no entanto, quando publico no nosso servidor ativo, o Application_Errorevento não é acionado.

Após alguns testes, eu consigo Application_Errordisparar quando defino customErrors="Off", no entanto, configurá-lo novamente para customErrors="On"interromper o disparo do evento novamente.

Alguém pode sugerir por Application_Errorque não seria customErrorsacionado quando ativado no web.config?


Estou tendo exatamente o mesmo problema. Também encontrei esta pergunta do SO: stackoverflow.com/questions/3713939/…, que sugere colocar o servidor IIS7 no modo clássico. Infelizmente isso não é uma opção para nós. Alguém tem alguma solução melhor?
Jesse Webb

Aqui é outra questão relacionada e é respostas (nenhum dos quais são aceitos) sugerem a não usar Application_Error () em tudo ... stackoverflow.com/questions/1194578/...
Jesse Webb

@ Gweebz Eu postei uma resposta sobre como eu consegui contornar isso, mas ainda não encontrei nenhuma documentação sólida sobre por que estava recebendo esse comportamento.
WDuffy

Eu adicionei uma resposta que explica por que o Application_Error()método não estava sendo chamado. Eu também expliquei minha solução final.
Jesse Webb

Realmente recomende este artigo para que os erros personalizados funcionem.
ThomasArdal

Respostas:


133

ATUALIZAÇÃO
Como essa resposta fornece uma solução, não a editarei, mas encontrei uma maneira muito mais limpa de resolver esse problema. Veja minha outra resposta para obter detalhes ...

Resposta original:
Eu descobri por que o Application_Error()método não está sendo chamado ...

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

Por padrão (quando um novo projeto é gerado), um aplicativo MVC possui alguma lógica no Global.asax.csarquivo. Essa lógica é usada para mapear rotas e registrar filtros. Por padrão, ele registra apenas um filtro: um HandleErrorAttributefiltro. Quando customErrors está ativado (ou por meio de solicitações remotas quando está definido como RemoteOnly), o HandleErrorAttribute diz ao MVC para procurar uma visualização de Erro e nunca chama o Application_Error()método. Não consegui encontrar documentação disso, mas está explicada nesta resposta em programmers.stackexchange.com .

Para obter o método ApplicationError () chamado para todas as exceções não tratadas, remova a linha que registra o filtro HandleErrorAttribute.

Agora, o problema é: Como configurar os customErrors para obter o que você deseja ...

A seção customErrors é padronizada como redirectMode="ResponseRedirect". Você também pode especificar o atributo defaultRedirect como uma rota MVC. Eu criei um ErrorController que era muito simples e alterei meu web.config para ficar assim ...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

O problema com esta solução é que ele faz um redirecionamento 302 para os URLs de erro e, em seguida, essas páginas respondem com um código de status 200. Isso leva o Google a indexar as páginas de erro, o que é ruim. Também não é muito compatível com a especificação HTTP. O que eu queria fazer não era redirecionar e substituir a resposta original com minhas visualizações de erro personalizadas.

Eu tentei mudar redirectMode="ResponseRewrite". Infelizmente, esta opção não suporta rotas MVC , apenas páginas HTML estáticas ou ASPX. Tentei usar uma página HTML estática no início, mas o código de resposta ainda era 200, mas pelo menos não foi redirecionado. Então eu tive uma idéia desta resposta ...

Decidi desistir do MVC pelo tratamento de erros. Eu criei um Error.aspxe um PageNotFound.aspx. Essas páginas eram muito simples, mas tinham um pedaço de mágica ...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

Este bloco informa a página a ser veiculada com o código de status correto. De grosso modo, na página PageNotFound.aspx, eu usei HttpStatusCode.NotFound. Mudei meu web.config para ficar assim ...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

Tudo funcionou perfeitamente!

Resumo:

  • Remova a linha: filters.Add(new HandleErrorAttribute());
  • Use o Application_Error()método para registrar exceções
  • Use customErrors com um ResponseRewrite, apontando para páginas ASPX
  • Responsabilize as páginas ASPX por seus próprios códigos de status de resposta

Há algumas desvantagens que notei com esta solução.

  • As páginas ASPX não podem compartilhar nenhuma marcação com os modelos do Razor, tive que reescrever a marcação padrão do cabeçalho e rodapé do nosso site para obter uma aparência consistente.
  • As páginas * .aspx podem ser acessadas diretamente pressionando seus URLs

Existem soluções alternativas para esses problemas, mas eu não estava preocupado o suficiente com eles para fazer qualquer trabalho extra.

Eu espero que isso ajude a todos!


2
+1! Solução realmente boa, mas quais podem ser as conseqüências da mistura do MVC com as páginas aspx?
Diego

2
Temos isso no PROD há alguns meses e não encontrei nenhum impacto negativo. O único problema foi que tivemos que mudar nosso IC e implantar para executar uma tarefa AspCompile MSBUILD porque o MVC não precisa disso, mas quando adicionamos os arquivos .as , eles exigiram. Pode haver outros problemas, mas eles não vieram à tona ainda ...
Jesse Webb

Eu tenho tentado usar essa abordagem no MVC4 e, aparentemente, só funciona para mim quando cheguei <customErrors mode="Off" />. Ter filters.Add(new HandleErrorAttribute());removido ou não não tem efeito.
Grzegorz Sławecki

na página aspx, você pode adicionar uma chamada ajax para chamar a visualização que deseja mostrar. Poupa de ter que código duplicado
Mike

71

Resolvi isso criando um ExceptionFilter e registrando o erro lá em vez de Application_Error. Tudo o que você precisa fazer é adicionar uma chamada no RegisterGlobalFilters

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}

6
+1 Se esta resposta funcionar, com certeza me parece a maneira mais elegante e talvez pretendida (pela estrutura MVC) para lidar com erros.
217306 Matt Hamsmith #

2
Isso funciona perfeitamente para mim e é muito mais organizado do que a resposta aceita aqui. Agradável!
Alex Warren

3
Isso funcionará para resolver o problema de exceções de log. Como você combinou isso com a exibição de páginas de erro personalizadas?
Jesse Webb

3
Eu tentei isso e funcionou muito bem, exceto no caso dos 404s. Para 404s, ele não mostraria a exibição Errors.cshtml, apenas forneceria um YSoD. Se 404s personalizados não forem necessários, esta solução é definitivamente mais limpa!
Jesse Webb

1
Eu gosto disso. Funciona com 'CustomErrors = On'
Illidan 22/07

36

Encontrei um artigo que descreve uma maneira muito mais limpa de criar páginas de erro personalizadas em um aplicativo da Web MVC3 que não impede a capacidade de registrar as exceções.

A solução é usar o <httpErrors>elemento da <system.webServer>seção.

Eu configurei meu Web.config assim ...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

Também configurei customErrorspara ter mode="Off"(como sugerido pelo artigo).

Isso faz com que as respostas sejam substituídas pelas ações de um ErrorController. Aqui está esse controlador:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

As visualizações são muito diretas, usei a sintaxe padrão do Razor para criar as páginas.

Isso por si só deve ser suficiente para você usar páginas de erro personalizadas com o MVC.

Eu também precisava do log de exceções, então roubei a solução do Mark usando um ExceptionFilter personalizado ...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

A última coisa que você precisa fazer é registrar o filtro de exceção no seu arquivo Global.asax.cs :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

Parece uma solução muito mais limpa do que a minha resposta anterior e funciona tão bem quanto eu sei. Eu gosto especialmente porque não parecia que estava lutando contra a estrutura MVC; esta solução realmente a alavanca!


6
Vale ressaltar que acho que esta solução funciona apenas no IIS 7 e mais recente; o elemento httpErrors foi adicionado recentemente.
Jesse Webb

Essa resposta não funcionou para mim, ele tem me, jogo que me uma horrível azul: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. tela de erro, não o meu pedido /Error/Indexpágina
Serj Sagan

@SerjSagan Acabei de tentar isso em um novo projeto para testar as etapas e funciona bem. Você está usando o IIS, IIS Express ou VS Dev Server? Isso não funcionará no VS Dev Server. Quando meu projeto foi definido para usar o VS Dev Server, não o IIS, notei erros na tela amarela em vez de páginas de erro personalizadas.
Jesse Webb

1
@SerjSagan Mas parece que você está recebendo erros de tela azul do IIS, em oposição aos erros clássicos de tela amarela. Isso me leva a supor que você está usando algum tipo de IIS. Em caso afirmativo, leia o artigo ao qual vinculei a primeira frase, especificamente a última seção, intitulada: "Algumas notas importantes". Ele diz:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb

3
@SerjSagan Alterar errorMode para DetalhadoLocalOnly ou Personalizado mostrará sua página.
Daniel P

8

No caso do ASP.NET MVC5, use

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

Você pode encontrá-lo em FilterConfig.csde App_Startpasta.


1

Eu gosto da resposta de Mark com o ExceptionFilter, mas outra opção, se você tiver todos os seus controladores derivados do mesmo controlador base, é simplesmente substituir o OnException no seu controlador base. Você pode fazer seu log e enviar por e-mail lá. Isso tem a vantagem de poder usar as dependências que você já injetou no seu controlador de base com seu contêiner de IoC.

Você ainda pode usar sua IoC com um IExceptionFilter, mas é um pouco mais complicado configurar suas ligações.


0

Até onde eu sei, você está passando o controle para a página especificada no parâmetro url e sua notificação de evento fica aqui, em vez de Application_Error

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

Muitas informações podem ser encontradas aqui: http://support.microsoft.com/kb/306355


Obrigado Chris, li a documentação e sugere que o Application_Error será chamado quando ocorrer um erro não tratado (o que eu espero) e que Server.ClearError () não seja chamado de seção web.config customErrors será o ponto final do tratamento. No entanto, ativar customErrors é o que está impedindo que o Application_Error seja acionado.
WDuffy

A documentação à qual você vinculou está falando sobre o aplicativo ASP.NET e parece que os aplicativos da Web MVC3 estão se comportando de maneira diferente.
Jesse Webb

0

Para contornar isso, acabei deixando os espelhos do cliente desativados e manipulando todos os erros do evento Application_Error em global.asax. É um pouco complicado com o MVC, como eu não queria retornar um redirecionamento 301, queria retornar códigos de erro adequados. Mais detalhes podem ser vistos no meu blog em http://www.wduffy.co.uk/blog/using-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/, mas o código final é listado abaixo...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

E aqui está o controlador

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}

Eu tentei sua solução, mas não funcionou para mim. Com as linhas Response.StatusCode = ###;, estava mostrando as páginas de erro do MVC embutidas em C:\inetpub\custerr\en-US. Também não gostei da idéia de chamar manualmente HttpHandlers ou Controllers do meu método Application_Error (). Ainda bem que você encontrou uma solução para o seu problema, sei que tipo de dor de cabeça isso me causou.
Jesse Webb

0

Esta entrada do blog me ajudou a:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

Se você estiver usando o IIS 7.0 ou superior, poderá alterar o arquivo Web.config para lidar com solicitações muito grandes. Existem algumas ressalvas, mas aqui está um exemplo:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Há detalhes adicionais sobre esses elementos do arquivo de configuração aqui:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

O código de status 404.13 é definido como "Comprimento do conteúdo muito grande". Uma coisa importante a observar é que o maxAllowedContentLengthé especificado em bytes. Isso é diferente da maxRequestLengthconfiguração encontrada na <system.web>seção especificada em kilobytes.

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

Observe também que o pathatributo deve ser um caminho absoluto quando responseModeestiver Redirect, portanto, acrescente o nome do diretório virtual, se relevante. As respostas informativas de Jesse Webb mostram como fazer isso responseMode="ExecuteURL", e eu acho que essa abordagem também funcionaria bem.

Essa abordagem não funcionará se você estiver desenvolvendo usando o Visual Studio Development Server (Cassini, o servidor da Web integrado ao Visual Studio). Suponho que funcionaria no IIS Express, mas não testei isso.


0

Eu estava tendo o mesmo problema em que Application_Error() não estava sendo atingido. Eu tentei de tudo, até que finalmente passei pelo que estava acontecendo. Eu tinha um código personalizado em um evento ELMAH que estava adicionando JSON ao email que ele envia e houve um erro nulo lá!

A correção do erro interno permitiu que o código continuasse no Application_Error()evento conforme o esperado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.