A quick search on the interwebs reveals the fact that people have very specific reasons from MultiTenancy to Custom paths not possible with the out of the box routing mechanisms.
Today we’ll pick an easy and superfluous one to demonstrate how to do a custom RouteBase implementation. We will see how we can re-direct non-standard URLs like html/asp and aspx files to a cleaner ‘Friendly Url’ format.
The Requirement
Handle extensions like html/pdf and redirect to Friendly URLs that ‘look like’ MVC generated URLs.
The Custom Route Implementation
We have to derive our own Route class from Route base and implement two methods1. GetRouteData: This method is called when Inbound URL Matching is required.
2. GetVirtualPath: This method is called when Outbound URL Generation is required
Step 1: To create a custom route base, we start off with a Basic Project template.
Step 2: Next we add a CustomRouteController to channel all the requests to. The controller simply puts the incoming URL in a label in our sample, but in reality, it could use the incoming URL and find out from a mapping database what the new equivalent URL was and do a RedirectPermanent action to a new Controller/ActionMethod too.
That’s with the Controller.
public class CustomRouteController : Controller
{
public ActionResult DirectCustomUrls(string customUrl)
{
if (customUrl.EndsWith("html"))
{
return new RedirectResult(customUrl);
}
else
{
ViewBag.ErrorMessage = "Unrecognized URL";
return View();
}
}
}
Step 3: Now let’s setup the Custom Route. The constructor by design takes an array of valid URLs that we will have to pass when we are creating the route for this.
The GetRouteData method is called when a new request is made. In this method, the Requested path is checked to see if it contains any of the URLs that were passed in the constructor. If anyone matches, we send back the required RouteData, if not, we return Null and MVC takes the Route resolution to the next registered route.
Point to note is - the mechanism to verify the incoming Request Information matches the ‘custom requirements’ has to be in the GetRouteData method. Our oversimplistic scenario has resulted in a rather petty check involving string matching.
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = string.Empty;
for (int i = 0; i < urls.Length; i++)
{
if (httpContext.Request.AppRelativeCurrentExecutionFilePath.Contains(urls[i]))
{
requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
break;
}
}
if (!string.IsNullOrEmpty(requestedURL))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "CustomRoute");
result.Values.Add("action", "DirectCustomUrls");
result.Values.Add("customUrl", requestedURL);
}
return result;
}
Step 4: Registering our Route.
1. We register our route as follows in the App_Data\RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new CustomRouteBase.Routing.CustomRouteBase(
"html",
"pdf"));
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Essentially we are taking over all URLs that contain an html or pdf and redirecting via our Route.
We add two default views for the Home Controller and the DynamicController. The DynamicController picks up the message from View Bag and displays it if any.
Let’s Run our app.
We initially are at the home page. Thereafter when we navigate to a random URL that ends with html we get redirected to our custom page via our CustomRouteBase.
Niceee.
Generating Outgoing URLs
Lets say now user needs to generate an HttpActionLink, to support this, we need to implement the GetVirtualPath method in our CustomRouteBase class. Once again, if we are unable to deal with the request, we let the routing system know by returning null. Otherwise, we return an instance of the VirtualPathData class.public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData result = null;
if (values.ContainsKey("html") || urls.Contains((string)values["html"], StringComparer.OrdinalIgnoreCase))
{
result = new VirtualPathData(this,
new UrlHelper(requestContext)
.Content((string)values["html"]).Substring(1));
}
return result;
}
Now we place the following markup in our Index.cshtml
<div>This is a URL:
@Html.ActionLink("Click me", "CustomUrl",
new { html = "~/html/OldFile.html" })
</div>
This results in the following ActionLink getting generated
We can manipulate the generated any way we want based on the final requirement.
Conclusion
With that we conclude this demo of how to Manipulate Route with a custom implementation of RouteBase. Next time we’ll see how we can create a custom RouteHandler and how it can be used.Download the entire source code of this article (Github)
The only thing that makes routing flexible in ASP.NET MVC is Attribute Routing. Without that routing in MVC is completely worthless.
ReplyDelete