ASP .NET MVC 3 - Filtros : Conceitos e utilização


Os filtros são atributos anexados às classes ou métodos dos controladores que injetam lógica extra ao processamento da requisição e permitem a implementação de funcionalidades relacionadas a autorização, log e cache de forma simples e elegante.

Os filtros usados no ASP .NET MVC são diferentes do Request.Filter da plataforma ASP .NET e dos objetos Response.Filter, que realizam transformações no stream de solicitação e resposta(uma atividade avançada e raramente realizada). Você pode usar Request.Filter e Response.Filter em uma aplicação MVC, mas, em geral, os filtros do ASP.NET MVC possuem outra abordagem.

O framework MVC suporta quatro diferentes tipos de filtros. Cada um permite introduzir uma lógica em diferentes pontos do fluxo de processamento da requisição.

Os quatro tipos de filtro estão descritos a seguir:

Tipos de filtros MVC :

Tipo do Filtro Interface Implementação padrão Descrição
Authorization IAuthorizationFilter AuthorizeAttribute É executado antes de qualquer outro filtro ou método Action
Exemplos de filtro de autorização ASP .NET MVC:
  • AuthorizeAttribute
  • RequireHttpsAttribute
  • ValidateInputAttribute
  • ValidateAntiForgeryTokenAttribute
  • ChildActionOnlyAttribute
Action IActionFilter ActionFilterAttribute É executado antes de depois do método Action do controlador

Possui dois métodos: OnActionExcecuting e OnActionExecuted

Result IResultFilter ActionFilterAttribute É executado antes de depois do método ActionResult.

Possui dois métodos: OnResultExcecuting e OnResultExecuted

Exception IExceptionFilter HandlerErrorAttribute Executa somente se outro filtro, o método Action, ou
o resultado de ação gerar uma exceção
Tabela 1.0 - tipos de filtros MVC

Antes do framework MVC invocar uma Action(ação), ele inspeciona a definição do método para ver se ele tem atributos que implementam as interfaces listadas na tabela 1.0. Se assim for, então no ponto apropriado do fluxo da solicitação, os métodos definidos por essas interfaces são invocados. O framework inclui os atributos de classe padrão que implementam as interfaces de filtro.

Obs: A classe ActionFilterAttribute implementa tanto a interface IActionFilter como a IResultFilter. Essa classe é abstrata, a qual obriga você a fornecer uma implementação. As outras classes, AuthorizeAttribute e HandleErrorAttribute, contêm características úteis e podem ser utilizadas sem a criação de uma classe derivada.

A implementação das interfaces na definidas na tabela 1.0 resulta em métodos adicionais na classe Controller conforme mostrado na tabela 2.0 seguir:

Método Descrição
OnActionExecuting Invocado pouco antes de um método Action ser executado
OnActionExecuted Invocado logo após que execução de um método Action foi terminado
OnAuthorization Invocado ao autorizar a execução de um método Action
OnException Invocado quando uma exceção ocorre em um método Action
OnResultExecuting Invocado logo após um Action Result é executado
OnResultExecuted Invocado logo após a execução de uma Action Result foi terminada
Tabela 2.0 - Métodos da classe Controller

Todos esses métodos são protected ou virtual e eles podem ser sobrescritos em sua classe controller para ter uma comportamento mais especializado.

Você pode aplicar um filtro a cada método individualmente ou a um controlador como um todo. No exemplo abaixo temos um trecho de código que mostra a aplicação do filtro de autorização Authorize à classe controladora e em cada método Action.

Usar o filtro na classe controladora equivale a usar o filtro em cada um dos métodos Action.

namespace Repositorio_EF.Controllers
{
  
 [Authorize]
    public class UsuarioController : Controller
    {
        [Authorize]
        public ActionResult Index()
        {
            return View();
        }
        [Authorize]
        public ActionResult LogOn()
        {
            return View();
        }
        //
      .......
      ......
  }
Os Filtros de Autorização, como o nome sugere, reforçam a sua política de autorização,
garantindo que os métodos de ação podem ser invocados somente por usuários autorizados.

Para checar se a sessão está autenticada, podemos utilizar o atributo IsAuthenticated,
conforme exemplo dado a seguir:

if (User.Identity.IsAuthenticated)
{
   ...
}

Você pode aplicar vários filtros, e pode misturar e combinar os níveis em que são aplicados, isto é, se eles são aplicados ao controlador ou a um método de ação individual.

No exemplo a seguir temos um trecho de código que usa 3 filtros diferentes:

    [Authorize]
    public class UsuarioController : Controller
    {
        [ShowMessage]
        [OutputCache(Duration=60)]
         public ActionResult Index()
        {
            return View();
        }
      .......
      ......
  }
  1. O filtro Authorize é aplicado a todas as Actions do controlador
  2. Os outros dois filtros são aplicados apenas ao método Action onde foram definidos

Filtros pré-definidos

O framework ASP .NET MVC vem com alguns filtros pré-definidos conforme mostrados na tabela 3.0 abaixo:

Filtro Descrição
AsyncTimeout Marca um método Action para executar assincronamente e terminar em um tempo de milesegundos especificado.
Authorize Marca um método Action para ser acessado somente por usuários com perfil e/ou autorização permitidas.
ChildActionOnly Marca um método Action para ser executado somente como um ação filha durante uma operação de renderização.
HandleError Marca um método Action para requerer um tratamento automático de qualquer exceção lançada durante sua execução.
OutputCache Marca um método Action como uma Action cuja saida precisa ser posta em cache.
RequireHttps Marca um método Action para requerer uma requisição segura. Se o método for invocado sobre HTTP o atributo força
o redirecionamento para a mesma URL mas sobre uma conexão HTTPS sempre que isso for possível.
ValidateAntiForgeryToken Marca um método Action para querer uma validação contra um token anti-falsificação na página para cada requisição POST.
Tabela 3.0 - Filtros pré-definidos

Cada método Controller pode ser decorado com múltiplos filtros e assim a ordem na qual eles são processados é importante.

Todos os atributos listados na tabela acima derivam da classe base FilterAttribute, o qual define a propriedade base Order. Essa propriedade indica a ordem na qual os atributos serão aplicados. Por padrão a propriedade Order tem o seu valor atribuído como -1 o que significa que a ordem não esta definida. Porém, qualquer filtro com uma ordem não especificada é sempre executado antes de um filtro com uma ordem definida, e, se você definir explicitamente a mesma ordem para dois ou mais filtros em um método vai ocorrer uma exceção.

Filtros Globais

Os filtros podem ser aplicados a métodos individuais ou à classe de controlador por inteira. Se os filtros são aplicados à classe do controlador, terão um efeito sobre todos os métodos Action expostos pelo controlador.

Os Filtros globais, em vez disso, são os filtros que, quando registrados na inicialização do aplicativo são automaticamente aplicados a qualquer ação de qualquer classe de controlador.

Por padrão, o filtro HandleError é globalmente registrado no global.asax, o que significa que ele fornece algumas capacidades de tratamento de exceção para quaisquer métodos de ação. (Como veremos no exemplo deste artigo)

Filtros globais são filtros de ação simples, que são apenas registrados de um modo diferente. Exemplo:

GlobalFilters.Filters.Add (novo HandleError ());

A coleção GlobalFilters é verificada pelo invocador da Action atual antes que cada ação seja chamada, e todos os filtros encontrados são adicionados à lista de filtros habilitados para o pré e pós-processo na ação.

Usando Action Filters

Um filtro Action ou de ação é uma classe que herda de FilterAttribute ou de uma das suas subclasses, geralmente ActionFilterAttribute, que adiciona os métodos OnActionExecuting, OnActionExecuted, OnResultExecuting e OnResultExecuted, proporcionando ganchos para código a ser executado, tanto antes como após o processamento da ação e do resultado.

Como os filtros de ação são subclasses da classe System.Attribute (quer através FilterAttribute ou uma de suas subclasses), eles podem ser aplicados a seus controllers e métodos de ação usando a sintaxe padrão do atributo de metadados:

   [NomeDoAtributo(Parametro = Valor)]
   public ActionResult ActionMethod()
   {
      //implementacao
   }
<NomeDoAtributo(Parametro:=Valor)> _
Public Function ActionMethod() As ActionResult
     ' implementacao
End Function
VB .NET C#

Isso faz dos filtros Action uma maneira fácil de adicionar uma funcionalidade frequentemente utilizada a seus controllers e métodos de ação, sem se intrometer no código do controlador, e sem realizar repetição desnecessária.

Vamos ver então como ela é implementada por padrão em uma aplicação ASP .NET MVC.

Implementando Filtros

Abra o Visual Web Developer 2010 Express e crie um novo projeto do tipo ASP .NET MVC 3 Web Application com o nome Mvc_Filtros. (nome não importa use qualquer nome que desejar.)

A seguir selecione o template Internet Application e o engine Razor e clique em OK;

Será criado o projeto contendo a estrutura exibida na janela Solution Explorer. Abaixo estamos visualizando o controller HomeController.cs:

A primeira vista não vemos nada no código referente a tratamento de erros. Correto ?

Mas não é bem assim...

Na verdade a aplicação esta usando o recurso dos Filtros Globais e esta pronta para fazer o tratamento de erros através do atributo HandleErrorAttribute.

Duvida ???

Então abra o arquivo Global.asax e veja o código abaixo:

using System.Web.Mvc;
using System.Web.Routing;

namespace Mvc_Filtros
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode,
    // visit http://go.microsoft.com/?LinkId=9394801


    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }


        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
    }
}

Observe o código destacado na cor azul.

Ao adicionar o atributo HandleErrorAttribute à coleção GlobalFilters.Filters, ele será aplicado a cada ação em nossa aplicação, e, qualquer exceção não tratada pelo nosso código fará com que ele seja invocado. Por padrão, ele irá simplesmente retornar uma view com o nome de Error.cshtml, que já foi criada e colocada na pasta Views->Shared.

Abaixo vemos o código da View Error.cshtml da pasta Views->Shared:

Vamos fazer a prova dos nove ????

Vamos adicionar então o código abaixo na Action About do controlador HomeController.cs para lançar uma exceção qualquer e ver o comportamento da aplicação:

Como exemplo estou lançando a exceção DivideByZeroException:

Executando a aplicação teremos:

 
 

Por que estamos recebendo a tela amarela de erro?

Porque, por padrão, CustomErrors está definido para RemoteOnly, o que significa veremos todos os detalhes de quaisquer erros quando executamos a aplicação localmente.

O filtro HandleErrors só é invocado quando CustomErrors está ativo. Para ver o filtro HandleErrors em ação quando executado localmente, precisamos adicionar a seguinte linha ao web.config, na seção system.web:

Executando o projeto novamente e clicando na aba About teremos:

Agora HandleError foi chamado e está retornando o modelo padrão de exibição de erro. No processo, HandleError marca também a exceção como sendo tratada, evitando assim a temida tela amarela da erro.

Agora vamos supor que a view de erro genérica é a melhor para a maioria dos cenários, mas queremos uma visão de erro mais específica quando tentamos dividir por zero. Podemos fazer isso adicionando o atributo HandleError ao nosso controlador ou ação, que irá substituir a versão global do atributo.

Primeiro, porém, vamos criar uma nova View para tratar o erro específico:

  • Clique com o botão direito do mouse em Views->Shared e selecione Add>View;
  • Especifique o nome como DivisaoPorZero
  • Marque a opção Create a strongly-typed view;
  • Selecione System.Web.Mvc.HandleErrorInfo, que é passado pelo filtro
    HandleError à view que está sendo chamado;
  • Clique no botão Add;

Usar uma view fortemente tipada simplifica o código da nossa view vinculando a view à instância HandleErrorInfo passada para a view, que pode ser acessada via sintaxe @Model.propertyname, como mostrado abaixo:

Agora volte ao controlador HomeController, e adicione o atributo HandleError sobre o método de Action, especificando que ele deve retornar a view DivisaPorZero:

Execute a aplicação e veja o resultado :

Mas agora temos um problema ...

O filtro ligado ao método Action About retornará o a View DivisaPorZero independentemente de qual exceção ocorre.

Felizmente, isso é fácil de corrigir, adicionando o parâmetro ExceptionType ao atributo HandleError:

[HandleError (Ver = "DivisaoPorZero", ExceptionType = typeof (DivideByZeroException))]

Agora o atributo HandleError anexado ao nosso método Action só será chamado para um DivideByZeroException, enquanto a versão global do atributo HandleError será invocado por quaisquer outras exceções em nossos controladores ou ações.

Você pode obter ainda mais controle sobre como e quando os filtros são invocados passando o parâmetro de ordem para o atributo, ou até mesmo criar seus próprios filtros personalizados. Por exemplo, você poderia criar um filtro personalizado que herda de FilterAttribute e implementa IExceptionFilter, em seguida, implementar o método de onException para fornecer o seu próprio controle de erro personalizado, como o registro de informações de erro, ou realizar um redirecionamento.

Fique a vontade para criar e implementar os seu próprio tratamento de erros usando os recursos dos Filtros aqui mostrado.

Conclusão :

Com essa pequena introdução eu dei uma visão geral em alguns dos aspectos mais importantes dos comportamentos dos filtros e em outro artigo pretendo mostrar na prática a aplicação destes conceitos na criação e utilização filtros.

Pegue o projeto completo aqui: Mvc_Filtros.zip

Rom 12:11 não sejais vagarosos no cuidado; sede fervorosos no espírito, servindo ao Senhor;
Rom 12:12
alegrai-vos na esperança, sede pacientes na tribulação, perseverai na oração;
Rom 12:13
acudi aos santos nas suas necessidades, exercei a hospitalidade;

Rom 12:14
abençoai aos que vos perseguem; abençoai, e não amaldiçoeis;

Referências:


José Carlos Macoratti