Creating “Short” Urls in ASP.NET MVC

I recently created a site to promote small local coffee shops (LocalCoffee.Rocks). Each coffee shop location has its own profile page. I wanted to make it easy for these shops to point people directly to this dynamic page without having to give them an ugly url containing domain/controller/action/parameterId. I figured out a way to short circut the routing information using the Application_BeginRequest() method in the Global.asax file.

First, in the database, under each shop locations record I have a varchar(20) field for their “shortUrl”. This the string that shops can promote to their clients to go directly to their profile. For example instead of accessing
http://localcoffee.rocks/Locations/LocationDetails/4 users can simply enter http://localcoffe.rocks/brewhaha to get to the same profile.

Once I had the value set in the db in the Application_BeginRequest() method to get the “controller” parameter from the url. The trick is that I do have other actual controllers that I want to fire and act just like they are designed in MVC. Thus, after I get the controller parameter I created a list and populated it with all of the names of the real controllers that are in the application. I did this with using reflection by calling the GetTypes() method from the Assembly class. After I had both the passed controller parameter and the list of physical controllers I simply check to see if the passed controller was in the list of physical controllers. If so I did nothing and the site continued to process as normal. If, however, the passed controller was not in the list it most likely is a shortUrl for a shop and so I passed that value to a function to handle that request. Below is the Application_BeginRequest() method.

        protected void Application_BeginRequest()
        {
            //get current url
            HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
            //break url up into routing sections(controller, actionresult, parameters)
            RouteData rd = RouteTable.Routes.GetRouteData(context);
            //get "controller" from route parameters
            string shortUrl = rd.GetRequiredString("controller").ToLower();

            //create list to store
            List<string> controllers = new List<string>();

            // Get list of all physical controllers in the application
            Assembly asm = Assembly.GetExecutingAssembly();
            var controllerNames = asm.GetTypes()
                .Where(type => typeof(Controller).IsAssignableFrom(type)).Select(t => t.Name.ToLower());

            // Add each controller to the controller list
            foreach (var item in controllerNames)
            {
                string nameOnly = item.Substring(0, item.Length - 10);
                controllers.Add(nameOnly);
            }

            //check if controller is not in the list and if not pass it to the shortUrl processing method
            if (!controllers.Contains(shortUrl))
            {
                //send shortUrl to method to get actual URL
                R(shortUrl);
            }
        }

In the shortUrl processing method I query the database for the passed value which should be located in the ShortUrl column of the database table for each shop location. Then, if a location was found I redirected the browser to the profile page using the full controller/action/parameterId url. If a location was not found the user could have mistyped the short url so I simply redirect them to a UrlNotFound page as seen below.

private void R(string ctrl)
        {
            // Query DB for the controller name passed in
            UnitOfWork uow = new UnitOfWork(new ApplicationDbContext());
            Location location = uow.LocationRepository.Get().Where(x => x.Shop.IsActive == true && x.ShortUrl == ctrl.ToLower()).FirstOrDefault();

            // If a URL is found the controller name was a short url
            if (location != null)
            { 
                // Redirect to the actual website
                Response.Redirect("http://localcoffee.rocks/Locations/LocationDetails/" + location.LocationId);
            }
            else
            {
                Response.Redirect("/UrlNotFound.html");
            }
        }

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *