Desenvolvimento - ASP. NET

Trabalhando com Limites de Rotas (Route Constraints) no ASP.NET MVC

Em aplicações Web é normal a necessidade de restringir o acesso à uma determinada URL para um usuário não autenticado por exemplo. Neste artigo, veremos como fazer este tipo de restrição (Route Constraints), bem como a restrição do roteamento de URLs cujo parâmetro não atenda os requisitos especificados (Via Regular Expressions).

por André Baltieri



Introdução

Utiliza-se limites de rotas para restringir o browser a na requisição de uma rota.

Podemos utilizar regular expressions para restringir uma rota.

Para melhor entender, vamos tomar como base a seguinte rota customizada, mostrada na Listagem 1.

   1: routes.MapRoute(

   2:     "Product", // Route name

   3:     "Product/{productId}", // URL with parameters

   4:     new { controller = "Product", action = "Detail" } // Parameter defaults

   5: );

Listagem 1 – Rota customizada para Produtos.

O Controller Product é mostrado na Listagem 2.

   1: using System.Collections.Generic;

   2: using System.Linq;

   3: using System.Web.Mvc;

   4: using LimitesDeRotas.Models;

   5:

   6: namespace LimitesDeRotas.Controllers

   7: {

   8:     public class ProductController : Controller

   9:     {

  10:         List<Product> produtos = new List<Product>{

  11:             new Product{ Id=1, Title="Pasta" },

  12:             new Product{ Id=2, Title="Rice" },

  13:             new Product{ Id=3, Title="Beans" }

  14:         };

  15:        

  16:         //

  17:         // GET: /Product/

  18:         public ActionResult Index()

  19:         {

  20:             return View();

  21:         }

  22:

  23:         public ActionResult Detail(int productId)

  24:         {

  25:             Product prod = (from p in produtos where p.Id == productId select p).First<Product>();

  26:             return View(prod);

  27:         }

  28:     }

  29: }

Listagem 2 – Controller ProductController.cs.

Note que a ação Details() do controller ProductController aceita um parâmetro único chamado productId, do tipo inteiro.

A rota customizada Product que criamos aceita as seguintes URLs:

· /Product/23

· /Product/7

Mas infelizmente, também aceita as seguintes URLs:

· /Product/blah

· /Product/wrong

Qualquer requisição cujo parâmetro não seja inteiro, causará um erro. O correto a se fazer é rotear apenas URLs que contenham o parâmetro productId como inteiro.

Para isso, utilizaremos um limite de rota (Route Constraint) na criação da rota para restringir URLs.

A Listagem 3 mostra a mesma rota (Product) porém, com um limite (Utilizando regular expressions) para informar que apenas valores numéricos no parâmetro productId serão rotados.

   1: routes.MapRoute(

   2:     "Product", // Route name

   3:     "Product/{productId}", // URL with parameters

   4:     new { controller = "Product", action = "Detail" }, // Parameter defaults

   5:     new { productId = @"\d+" }

   6: );

Listagem 3 – Rota customizada com limite.

Note que apenas adicionamos um parâmetro a mais (new { productId = @"\d+" }), que possui um regular expression, e este diz que podemos ter infinitos caracteres, desde que sejam numéricos.

A expressão regular \d+ roteia URLs cujo parâmetro tenha 1 ou mais inteiros, o que implica no roteamento das seguintes URLs:

· /Product/7

· /Product/2010

Mas NÃO no roteamento das URLs:

· /Product/learnmvc

· /Product/test

· /Product

Estas requisições serão manipuladas por outra rota ou, se não encontrar nenhuma rota exibirá o erro “The resource could not be found“.

Desta mesma forma, podemos utilizar regular expressions para validar E-mails, como mostrado na Listagem 4.

   1: routes.MapRoute(

   2:     "User", // Route name

   3:     "User/{email}", // URL with parameters

   4:     new { controller = "User", action = "Detail" }, // Parameter defaults

   5:     new { email = @"^[\w\.=-]+@[\w\.-]+\.[\w]{2,3}$", isLocal = new LocalhostConstraint() }

   6: );

Listagem 4 – Limite que aceita apenas E-mails como parâmetro.

Limites de Rotas Customizadas

Um limite de rota personalizada permite a prevenção de uma rota ser mapeada a menos que a condição customizada seja a rota requisitada.

Para isto, basta implementar a interface IRouteConstraint, que contém apenas um método:

   1: bool Match(

   2:     HttpContextBase httpContext,

   3:     Route route,    

   4:     string parameterName,    

   5:     RouteValueDictionary values,

   6:     RouteDirection routeDirection )

O método retorna um valor Booleano. Se retornar false, a rota associada com o limite não combina com a requisição do browser.

AListagem 5, contém um exemplo da criação de uma constraint.

   1: using System.Web;

   2: using System.Web.Routing;

   3:

   4: namespace LimitesDeRotas.Contraints

   5: {

   6:     public class RequireAuthConstraint : IRouteConstraint

   7:     {

   8:         public bool Match(

   9:             HttpContextBase httpContext,

  10:             Route route,

  11:             string parameterName,

  12:             RouteValueDictionary values,

  13:             RouteDirection routeDirection)

  14:         {

  15:             return httpContext.Request.IsAuthenticated;

  16:         }

  17:     }

  18: }

Listagem 5– Constraint para proibir acesso de usuários não autenticados.

Nota: Para esta constraint, criei uma nova pasta na raiz da aplicação com o nome “Constraints” e criei o arquivo da Listagem 5 com o nome RequireAuthConstraint.cs.

Com o limite criado, podemos utilizá-lo no Global.asax como fizemos previamente com os regular expressions. Veja na Listagem 6.

   1: routes.MapRoute(

   2:     "Product", // Route name

   3:     "Product/{productId}", // URL with parameters

   4:     new { controller = "Product", action = "Detail" }, // Parameter defaults

   5:     new { isLocal = new RequireAuthConstraint(), productId = @"\d+" }

   6: );

Listagem 6– Limite de rota customizado para acesso somente de usuários logados.

Estes limites continuam definidos dentro do Global.asax. No exemplo, utilizamos um limites que não permite requisições a página Product sem o usuário estar logado.

Por exemplo, o URL /Product/Detail irá falhar caso o usuário não esteja logado. Podemos ter mais de um limite, como no caso, ainda temos o limite para aceitar somente parâmetros numéricos no productId (Separado por vírgula).

É importante notar que outras rotas definidas no Global.asax podem servir para a mesma requisição. Um limite previne uma rota em particular de ser comparada a requisição, mas não todas as rotas definidas no Global.asax.

Sendo assim, as outras rotasdevem estarcomentadas, ou conter o mesmo limite,pois caso contrário, a mesma conseguiria manipular requisições para o controller.

Conclusão

Limites de rotas nos permitir ter o controle de quais rotas devemos rotear ou não. Além disso, podemos utilizar estes limites para negar acesso de usuários não autenticados, por exemplo.

Referências

ASP.NET MVC
http://www.asp.net/mvc

Até o próximo artigo!

André Baltieri

André Baltieri - Trabalha com desenvolvimento de aplicações web a mais de 7 anos, e com ASP.NET desde 2003. É líder da comunidade Inside .NET (http://www.insidedotnet.com.br/) e do projeto Learn MVC .NET (http://learn-mvc.net/). Bacharelando em Sistemas de Informação, atualmente trabalha com desenvolvimento e suporte de aplicações web em ASP.NET/C# em projetos internacionais e ministra treinamentos e consultorias sobre a plataforma .NET.