URL dinâmica é uma grande funcionalidade no MVC. URL amigável é ainda melhor. A solução desse artigo acredito que seja a melhor maneira de trabalhar com URLs amigáveis.
Vamos definir algumas premissas:
1 – As URLs precisam ser guardadas em um repositório. Isso significa que eu quero poder alterar e criar mais URL quando quiser de forma fácil;
2 – Uma ou mais URL pode apontar para o mesmo Controller/Action. Quer dizer que eu posso criar apelido para as URLs;
3 – Se uma URL não existir no meu repositório, tente resolver a URL usando o comportamento padrão do MVC Controller/Action. O comportamento padrão ainda funcionará;
4 – A URL pode ou não conter um ID ao final da URL. Significa que a última parte da URL pode ser um ID número inteiro;
Primeiro de tudo, o MVC não tem uma funcionalidade integrada e já pronta para URL dinâmica e amigável, nós temos que implementar.
Para a nossa solução precisaremos de:
1 – Um projeto MVC ;
2 – Uma classe para controlar as requisições de rotas;
3 – Um repositório de rotas;
4 – Controllers e views;
PS: Eu não irei usar um banco de dados para guardar as URLs mas irei usar o design patter de Repository e dependency resolver para resolver a dependência. Assim futuramente podemos mudar apenas uma parte do código para colocar o banco de dados sem afetar todo o resto.
A classe que resolver e identifica uma URL.
Handlers/UrlHandler.cs
public sealed class UrlHandler { public static UrlRouteData GetRoute(string url) { url = url ?? "/"; url = url == "/" ? "" : url; url = url.ToLower(); UrlRouteData urlRoute = null; using (var repository = DependencyResolver.Current.GetService<IRouteRepository>()) { var routes = repository.Find(url); var route = routes.FirstOrDefault(); if (route != null) { route.Id = GetIdFromUrl(url); urlRoute = route; urlRoute.Success = true; } else { route = GetControllerActionFromUrl(url); urlRoute = route; urlRoute.Success = false; } } return urlRoute; } private static RouteData GetControllerActionFromUrl(string url) { var route = new RouteData(); if (!string.IsNullOrEmpty(url)) { var segmments = url.Split('/'); if (segmments.Length == 1) { route.Id = GetIdFromUrl(url); route.Controller = segmments[0]; route.Action = route.Id == 0? (segmments.Length == 2? segmments[1] : route.Action) : route.Action; } } return route; } private static long GetIdFromUrl(string url) { if (!string.IsNullOrEmpty(url)) { var segmments = url.Split('/'); if (segmments.Length == 1) { var lastSegment = segmments[segmments.Length - 1]; long id = 0; long.TryParse(lastSegment, out id); return id; } } return 0; } }
Classe de controle de rotas que recebe todas as requisições.
Handlers/UrlRouteHandler.cs
public IHttpHandler GetHttpHandler(RequestContext requestContext) { var routeData = requestContext.RouteData.Values; var url = routeData["urlRouteHandler"] as string; var route = UrlHandler.GetRoute(url); routeData["url"] = route.Url; routeData["controller"] = route.Controller; routeData["action"] = route.Action; routeData["id"] = route.Id; routeData["urlRouteHandler"] = route; return new MvcHandler(requestContext); }
A configuração das rotas.
App_Start/RouteConfig.cs
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "IUrlRouteHandler", "{*urlRouteHandler}").RouteHandler = new UrlRouteHandler(); } }
Classes de repositório que serão armazenadas as URL amigável.
Repository/IRouteRepository.cs
public interface IRouteRepository : IDisposable { IEnumerable<RouteData> Find(string url); }
Repository/StaticRouteRepository.cs
public class StaticRouteRepository : IRouteRepository { public void Dispose() { } public IEnumerable<RouteData> Find(string url) { var routes = new List<RouteData>(); routes.Add(new RouteData() { RoouteId = Guid.NewGuid(), Url = "how-to-write-file-using-csharp", Controller = "Articles", Action = "Index" }); routes.Add(new RouteData() { RoouteId = Guid.NewGuid(), Url = "help/how-to-use-this-web-site", Controller = "Help", Action = "Index" }); if (!string.IsNullOrEmpty(url)) { var route = routes.SingleOrDefault(r => r.Url == url); if (route == null) { route = routes.FirstOrDefault(r => url.Contains(r.Url)) ?? routes.FirstOrDefault(r => r.Url.Contains(url)); } if (route != null) { var newRoutes = new List<RouteData>(); newRoutes.Add(route); return newRoutes; } } return new List<RouteData>(); } }
Eu criei duas URL. Uma URL irá apontar para o HelpController e a outra para ArticlesController.
Dependência de injeção do repositório. Eu uso Ninject para configurar isso.
App_Start/NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel) { kernel.Bind<Repository.IRouteRepository>().To<Repository.StaticRouteRepository>(); }
Bom é isso. Esperto que tenha ajudado.
Abaixo encontrará alguns links úteis.
URL amigável e dinâmica no MVC
URL amigável: Perguntas, sugestões ou críticas são bem vindas. Boa sorte!
Faça download completo do código fonte no github. |