Category - Projects

URL Routing With Templates

I had toyed around a bit with url routing for the Basic CMS with Routing project that I did for KC Web Pros. When constructing that I had hardcoded url routes in the Global.asax. I knew that for this project, it needed to be as dynamic as possible.

So, I started off by building a database that had a "templates" table. The table has a name, url, and path. I used that first to build the url route in the Global.asax.

Then as I played around with things some more, most especially as I got into the blog engine I built later, I realized I needed to go farther. I needed some optional and default values as well.

That meant adding a second table for template variables.

So, I added the extra code for that, it took a bit of digging to determine how to pass in the defaults and constraints, but here is what I ended up with:

    public static void RegisterRoutes(System.Web.Routing.RouteCollection routes)
    {

        //add the ignore for the .axd. This is nessecary for a lot of things, most notable some of the asp.net ajax cntrols.
        routes.Ignore("{resource}.axd/{*pathInfo}");
        //add a route for the 404 error
        routes.MapPageRoute("404Error", "404", "~/404.aspx", true);        
        //setup some route value dictionaries that we will use later
        RouteValueDictionary defaults = default(RouteValueDictionary);
        RouteValueDictionary constraints = default(RouteValueDictionary);

        //query the template info, and also grab the count of variables
        SqlConnection conn = new SqlConnection(WebConfigurationManager.ConnectionStrings["TheConnectionString"].ConnectionString);
        SqlCommand cmd = new SqlCommand("SELECT t.[TemplateID], [TemplateName], [TemplateURL], [TemplateFilePath], " +
            "(SELECT COUNT(*) FROM TempVariables tv WHERE tv.[TemplateID] = t.[TemplateID]) AS VariableCount" +
            " FROM Temps t ORDER BY [TemplateUrl] DESC", conn);
        conn.Open();
        SqlDataReader rdr = cmd.ExecuteReader();
        while (rdr.Read())
        {
            RouteValueDictionary dataTokens = new RouteValueDictionary();
            dataTokens.Add("template-name", rdr["TemplateName"].ToString());
                        
            if (rdr["VariableCount"] == DBNull.Value)
            {
                //no variables to process, so this should be easy!
                routes.MapPageRoute(rdr["TemplateName"].ToString(), rdr["TemplateURL"].ToString(), rdr["TemplateFilePath"].ToString(), true, defaults, constraints, dataTokens);
            }
            else
            {
                //we have variables to process!
                string tempUrl = rdr["TemplateURL"].ToString();//start with the base value, and now let's add to it!
                System.Web.Routing.RouteValueDictionary defValues = new System.Web.Routing.RouteValueDictionary(); 
                
                SqlConnection connVar = new SqlConnection(WebConfigurationManager.ConnectionStrings["SuperNinjaCMS"].ConnectionString);
                //get all the variables for this template so that we can add them to the defvalues dictionary.
                SqlCommand cmdVar = new SqlCommand("SELECT [Name], [IsCatchAll], [DefaultValue] FROM [TempVariables] WHERE " +
                    "[TemplateID] = @TemplateID ORDER BY [ListOrder]", connVar);
                cmdVar.Parameters.AddWithValue("TemplateID", (int)rdr["TemplateID"]);
                connVar.Open();
                SqlDataReader rdrVar = cmdVar.ExecuteReader();
                
                while (rdrVar.Read())
                {
                    if (rdrVar["DefaultValue"] != DBNull.Value)
                    {
                        defValues.Add(rdrVar["Name"].ToString(), rdrVar["DefaultValue"].ToString());
                    }
                    
                    if (!String.IsNullOrEmpty(tempUrl))
                    {
                        tempUrl += "/";
                    }
                    
                    tempUrl += "{";

                    //if this is a catch all, add the syntax to the string for that.
                    if ((bool)rdrVar["IsCatchAll"])
                    {
                        tempUrl += "*";
                    }
                    tempUrl += rdrVar["Name"].ToString() + "}";
                }
                connVar.Close();

                //okay, we have built the def values, see if that is empty
                if (defValues != null)
                {
                    routes.MapPageRoute(rdr["TemplateName"].ToString(), tempUrl, rdr["TemplateFilePath"].ToString(), true, defValues, constraints, dataTokens);
                }
                else
                {
                    routes.MapPageRoute(rdr["TemplateName"].ToString(), tempUrl, rdr["TemplateFilePath"].ToString(), true, defaults, constraints, dataTokens);
                }
                
            }//end if we have variables
        }//end template reader
        conn.Close();
    }//end register routes function     

This is pretty nasty to look at first, and I've added in some code for the constraints though I don't tinker with that at all yet. But, I wanted the built in functionality so if I choose to add it later based on the CMS needs, it'll be easy to implement.

The only downside to this code is that if I add a new template to the system, the application has to be rebuilt. This is a downside I decided was acceptable as I do not forsee needing new templates very often.

Starting off, I've got a template for the main page, a template for content pages (about, resume), a template for contact, and a template for projects.

You must be logged in to comment.