Neste post vou comentar um pouco sobre a interface IRouteHandler do ASP.NET MVC e como criar uma classe que a implemente. O objetivo é criar uma classe customizada para manipular a rota da aplicação. Esta é uma das inúmeras formas de estender a sua aplicação. Mas antes, vou explicar um pouco sobre como funciona uma requisição numa aplicação ASP.NET MVC.
Toda requisição recebida no ASP.NET MVC passa por um objeto chamado UrlRoutingModule, que é um módulo HTTP. Este módulo analisa a requisição e seleciona qual rota será utilizada. Esta rota é um objeto de uma classe que herda de RouteBase, e é geralmente uma instância da classe Route.
Uma vez obtido o objeto Route, o UrlRoutingModule obtém o objeto que implementa a interface IRouteHandler, que está associada na classe da rota. Por padrão, em uma aplicação MVC, será uma instância de MvcRouteHandler. A instância de IRouteHandler cria o objeto IHttpHandler, que por padrão será a classe MvcHandler. Por fim, o MvcHandler selecionará o controller que irá manipular a requisição.
Como exemplo, vou utilizar o seguinte cenário. Considere que o seu site possua uma área restrita à determinados países. Por exemplo, determinado país não poderá acessar determinada área do seu site.
O primeiro passo será criar uma classe que implemente a interface IRouteHandler. Esta interface possui um único método chamado GetHttpHandler, que recebe um RequestContext como parâmetro e retorna IHttpHandler. A classe que será criada chamará RestricaoPaiRouteHandler. Ela receberá em seu construtor uma lista de códigos de países que não estão autorizados à acessar o meu site. E no método GetHttpHandler será criada uma instância de uma classe que implementa IHttpHandler, que também será criada posteriormente.
using System.Collections.Generic; using System.Web; using System.Web.Routing; namespace CustomRoute.App_Start { public class RestricaoPaisRouteHandler : IRouteHandler { #region Campos private Listpaises; #endregion #region Construtor public RestricaoPaisRouteHandler(List paises) { this.paises = paises; } #endregion #region Métodos public IHttpHandler GetHttpHandler(RequestContext requestContext) { BloqueioIpHandler handler = new BloqueioIpHandler(this.paises, requestContext); return handler; } #endregion } }
Em seguida, será criada a classe BloqueioIpHandler herdando de MvcHandler. A classe MvcHandler já implementa IHttpHandler. Neste caso iremos apenas sobrescrever a sua implementação. Será sobrescrito o método BeginProcessRequest com a seguinte lógica. Será chamado um método ObterCodigoPais, que através do IP irá retornar a sigla do país que está realizando o acesso ao site. E se o país estiver na lista de países bloqueados será criada uma exceção.
using System; using System.Collections.Generic; using System.Web; using System.Web.Mvc; using System.Web.Routing; using System.Xml.Linq; namespace CustomRoute.App_Start { public class BloqueioIpHandler : MvcHandler { #region Campos private Listpaises; #endregion #region Construtor public BloqueioIpHandler(List paises, RequestContext requestContext) : base(requestContext) { this.paises = paises; } #endregion #region Métodos private string ObterCodigoPais(string ip) { string query = string.Format("http://api.hostip.info/?ip={0}", ip); XDocument doc = XDocument.Load(query); XNamespace defaultNamespace = doc.Root.GetDefaultNamespace(); XNamespace xNamespace = doc.Root.GetNamespaceOfPrefix("gml"); string pais = doc.Root.Element(xNamespace + "featureMember") .Element(defaultNamespace + "Hostip").Element(defaultNamespace + "countryAbbrev").Value; return pais; } protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) { string pais = ObterCodigoPais(httpContext.Request.UserHostAddress); if (this.paises.Contains(pais)) httpContext.AddError(new Exception("Desculpe! Você não pode acessar esta página a partir do seu país.")); return base.BeginProcessRequest(httpContext, callback, state); } #endregion } }
Após a criação das classes, é necessário registrar uma nova rota e definir a classe criada para manipular a mesma. Para isto altere a classe RouteConfig localizada na parta App_Start do projeto. Será definido uma nova rota, além da padrão, e nela será definida a propriedade RouteHandler.
using CustomRoute.App_Start; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace CustomRoute { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Restrito", url: "Restrito/{action}", defaults: new { controller = "Restrito", action = "Index" } ).RouteHandler = new RestricaoPaisRouteHandler(new List() { "XX" }); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } }
Agora ao acessar o controller Restrito será exibida uma mensagem de erro, informando que o país não está autorizado. No exemplo foi definido a sigla XX pois é a sigla que será retornada quando informado um IP local.
Quero ressalvar que este é um simples exemplo de como criar classe que faça um tratamento customizado da rota. Isto possibilita fazer qualquer tratamento da requisição. Há também outras formas de realizar o bloqueio por região, como por exemplo fazer o controle no próprio controller. Para fazer o exemplo foi utilizado o Visual Studio 2015 e o MVC 5.2.
O código fonte de exemplo está disponível no GitHub. Para acessar clique aqui.