Dynamic URL is a great feature working with MVC. Friendly URL are even better. The following approach i think is best way to work with friendly URL.
So let’s define some premises:
1 – The URLs must be stored in a Repository. It means I want to change and create new urls in my repository;
2 – One or more URL can be pointed to the same Controller/Action. It means I want to have alias for URLs;
3 – If an URL does not exists in my Repository, try to resolve using MVC Controller/Action default behavior. It means the MVC default behavior will still work;
4 – The URL can or not contain an ID at the end. It means that last segmment of those URLs can be a long ID number;
First of all, MVC does not have a built in feature for dynamic and friendly URL. You must write your own custom code.
For the solution we will need:
1 – A MVC project;
2 – A class to handle route requests;
3 – A route repository;
4 – Controllers and views;
PS: I will not use a database to store those URLs but I will use the repository pattern and dependency resolver to configure it. So, you can create a database repository in future.
Class that identify a 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; } }
Route Handler that handles all requests.
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); }
The route handler configuration.
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(); } }
Repository classes
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>(); } }
I have created 2 URL. One Url will point to Help Controller and another for Articles Controller.
The dependency resolver configuration. I use Ninject to do that.
App_Start/NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel) { kernel.Bind<Repository.IRouteRepository>().To<Repository.StaticRouteRepository>(); }
This is it. I hope it helped.
Dynamic and Friendly URL using MVC
Friendly URL in MVC: Questions, suggestions and feedbacks will be appreciated. Good luck!
Do complete download of the source code on github. | |
Try demo online of that application on codefinal. |