Tratamento de erros em aplicações ASP.NET MVC
Este artigo tem por objetivo abordar o tratamento de erros em aplicações ASP.NET MVC, a partir do uso de um mecanismo conhecido como Exception Filter.
Introdução
Tratar adequadamente os erros é uma característica fundamental de uma aplicação Web bem projetada. Além de inibir a exposição de aspectos técnicos desnecessários aos usuários de um site, esta abordagem procura evitar que tais dados sirvam de base para a condução de ataques explorando vulnerabilidades. Em ASP.NET MVC este tipo de comportamento pode ser implementado através da utilização da classe HandleErrorAttribute (namespace System.Web.Mvc).
Do ponto de vista prático, o atributo HandleErrorAttribute representa uma implementação da interface IExceptionFilter (namespace System.Web.Mvc). Já esta última construção corresponde à estrutura básica para o uso de um mecanismo de interceptação conhecido como Exception Filter, por meio do qual torna-se possível a exibição de páginas de erro mais amigáveis.
Nas próximas seções será demonstrada a utilização do atributo HandleErrorAttribute, partindo para isto de um exemplo envolvendo a exibição de mensagens de erro customizadas em uma aplicação ASP.NET MVC 5.
Implementando a aplicação de exemplo
A fim de demonstrar o tratamento de erros em aplicações MVC, será criada uma aplicação empregando os seguintes recursos:
- O Microsoft Visual Studio Community 2015 como IDE de desenvolvimento;
- O .NET Framework 4.6;
- O Microsoft ASP.NET MVC 5.
Para o exemplo aqui proposto criar um projeto do tipo “ASP.NET Web Application” chamado “TesteExcecoesMVC”:
Selecionar na sequência o template “MVC”:
Para habilitar a exibição de mensagens customizadas será necessário alterar o arquivo Web.config da aplicação, como indicado na próxima listagem. A fim de cumprir tal objetivo alterar no elemento customErrors o valor do atributo mode para “On”:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<system.web>
...
<customErrors mode="On" defaultRedirect="Error" />
</system.web>
...
</configuration>
A utilização de uma página de erro customizada e comum a toda uma aplicação depende da definição de um Global Filter. A ativação deste tipo de filtro é realizada através do método RegisterGlobalFilters, o qual se encontra declarado no tipo FilterConfig de uma aplicação MVC. Uma instância de HandleErrorAttribute pode ser empregada conforme indicado na listagem a seguir, em que a propriedade View da mesma foi preenchida com valor “ErroNaoMapeado” (este último correspondendo a uma View de mesmo nome):
using System.Web.Mvc;
namespace TesteExcecoesMVC
{
public class FilterConfig
{
public static void RegisterGlobalFilters(
GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute()
{
View = "ErroNaoMapeado"
});
}
}
}
Já na próxima listagem está o código que implementa a View ErroNaoMapeado.cshtml. A propriedade Model neste caso será uma referência para um objeto do tipo HandleErrorInfo (namespace System.Web.Mvc), com esta última classe encapsulando informações úteis para o tratamento de um erro originado em uma Action: as propriedades ControllerName e ActionName permitem identificar, respectivamente, o Controller e a Action que originaram uma exceção; a propriedade Exception, por sua vez, retorna a exceção não capturada e que resultou de uma falha na execução de uma Action.
@model System.Web.Mvc.HandleErrorInfo
<h2>Ocorreu um erro não mapeado durante a execução
da última ação...</h2>
<p>
<b>Controller que originou o erro:</b> @Model.ControllerName
</p>
<p>
<b>Action:</b> @Model.ActionName
</p>
<p>
<b>Tipo da Exceção:</b> @Model.Exception.GetType().FullName
</p>
OBSERVAÇÃO: todas as Views utilizadas na exibição de mensagens de erro customizadas foram criadas no caminho \Views\Shared\ do projeto de testes.
O atributo HandleErrorAttribute também pode ser empregado com vistas a um tratamento de exceções mais elaborado. A propriedade ExceptionType (utilizada em conjunto com a propriedade View) permite o redirecionamento para Views específicas, considerando para isto o tipo do erro que ocorreu em uma Action. Tal prática está exemplificada na listagem a seguir, em que ajustes foram realizados na classe HomeController:
- A Action ExemploErroNaoMapeado irá lançar uma instância do tipo Exception (namespace System) quando acessada. Por não existir nenhum mapeamento que associe este erro a uma View específica, a mensagem que consta no arquivo ErroNaoMapeado.cshtml deverá ser exibida;
- Quanto à Action ExemploMapeamentoErros, uma chamada ao método Next a partir de uma instância do tipo Random (namespace System) poderá produzir como resultado os valores 1, 2 ou 3. Caso esta ação retorne 1, uma exceção do tipo InvalidOperationException (namespace System) será lançada. Os demais valores farão com em que uma referência de HttpException produza um erro. Em ambos os casos o mapeamento do tipo da exceção e de sua View correspondente aconteceu através de instruções envolvendo o uso de HandleErrorAtribute.
using System;
using System.Web;
using System.Web.Mvc;
namespace TesteExcecoesMVC.Controllers
{
public class HomeController : Controller
{
...
public ActionResult ExemploErroNaoMapeado()
{
throw new Exception(
"Exemplo envolvendo o lançamento de uma exceção não mapeada.");
}
[HandleError(ExceptionType = typeof(InvalidOperationException),
View = "OperacaoInvalida")]
[HandleError(ExceptionType = typeof(HttpException),
View = "SolicitacaoHTTPInvalida")]
public ActionResult ExemploMapeamentoErros()
{
if (new Random().Next(1, 3) == 1)
{
throw new InvalidOperationException(
"Exemplo de operação inválida.");
}
else
{
throw new HttpException(
"Exemplo de processamento de solicitação HTTP inválida.");
}
}
}
}
OBSERVAÇÃO: por convenção, nomes de classes que representam atributos terminam com o sufixo Attribute. A utilização de expressões que envolvam um atributo vinculando o mesmo a uma estrutura de código dispensa o uso de tal sufixo ao final do nome. Logo, ao se empregar o atributo HandleErrorAttribute, o método marcado com essa construção estará associado apenas a uma instrução com o valor “HandleError” (entre colchetes).
O código que define a View OperacaoInvalida está na próxima listagem:
<h2>
Ocorreu uma operação inválida!
</h2>
Já na listagem a seguir está a definição da View SolicitacaoHTTPInvalida:
<h2>
Exemplo de solicitacao HTTP inválida.
</h2>
Por fim, na listagem seguinte está o trecho de código do arquivo Index.cshtml com os links associados às Actions de teste:
...
<div class="jumbotron">
<h1>Mensagem de erro customizadas em ASP.NET MVC</h1>
</div>
<p>
Selecione a Action para teste:
</p>
<ul>
<li>
@Html.ActionLink("Exemplo envolvendo erro não mapeado",
"ExemploErroNaoMapeado", "Home")
</li>
<li>
@Html.ActionLink("Exemplo envolvendo mapeamento de exceções por tipo",
"ExemploMapeamentoErros", "Home")
</li>
</ul>
Testes
Será necessário agora iniciar a execução da aplicação TesteExcecoesMVC. Para um primeiro teste utilizar a seguinte URL (o valor “58712” corresponde a uma porta disponível no momento da criação da solução):
http://localhost:58712/
Na próxima imagem está a tela inicial do projeto:
Ao se acionar a Action ExemploErroNaoMapeado a seguinte mensagem será produzida como resultado:
Já os testes com a Action ExemploMapeamentoErros poderão retornar uma mensagem como a que consta na próxima imagem:
Ou ainda:
Conclusão
Conforme demonstrado ao longo deste artigo, Exception Filters representam uma alternativa simples e bastante prática para a configuração de mensagens de erro customizadas. Partindo do uso de Views compartilhadas por todo um projeto MVC, este mecanismo permite que problemas sejam reportados aos usuários de uma forma amigável e sem a exposição de detalhes técnicos.
Referências
Filtering in ASP.NET MVC
https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx
HandleErrorAttribute Class
https://msdn.microsoft.com/pt-br/library/system.web.mvc.handleerrorattribute(v=vs.118).aspx