+1-888-365-2779
Try Now
More in this section

Forums / General Discussions / URL Routing in Sitefinity 5.0

URL Routing in Sitefinity 5.0

12 posts, 1 answered
  1. vitako
    vitako avatar
    22 posts
    Registered:
    28 Feb 2010
    07 May 2012
    Link to this post
    Hello Sitefinity Team,
    Thanks for all the good work you've done so far with sitefinity.

    We developed couple of new custom modules for our wesite, and now it'is time to find a solution for seo friendly urls. URL Routing comes to mind right away since it's very easy to do it in asp.net 4.0 framework. However after trying the standard asp.net 4.0 way for routing we realized that Sitefinity uses its own route handlers to serve the pages. After the reasearch we found few posts on these forums with workarounds. Please see the code below that we were able to put together using those samples:

    public class DCOCustomRoute : SitefinityRoute 
    {
        public override System.Web.Routing.RouteData GetRouteData(HttpContextBase httpContext) 
       {
          //get the path from the httpContext variable and parse it 
          var virtuallPath = this.GetVirtualPathInternal(httpContext); 
      
          //where directory/categories is the name of the Virtual page in sitefinity 
          if (virtuallPath.Contains("directory/categories")) 
          {
             //parse the acutal path to find the PageSiteNode from the sitemap provider 
             var sitemapProvider = this.GetSiteMapProvider(); 
      
             if (sitemapProvider == null
             {
                return null
             }
      
             bool isAdditional; 
             string[] pars; 
      
             //where /directory/categories is the Virtual Path in SiteFinity 
             var node = sitemapProvider.FindSiteMapNode("/directory/categories", false, out isAdditional, out pars); 
      
             if (node != null
             {
                return this.GetRouteDataInternal(pars, httpContext.Request.QueryString, node); 
             }
          }
      
          return base.GetRouteData(httpContext); 
       }
      
       public static void RegisterType() 
       {
          ObjectFactory.Container.RegisterType<SitefinityRoute, DCOCustomRoute>(); 
       }
      
       protected override SiteMapBase GetSiteMapProvider() 
       {
          var siteMapProvider = base.GetSiteMapProvider(); 
          SystemManager.CurrentHttpContext.Items[SiteMapBase.ProviderKey] = siteMapProvider; 
          return siteMapProvider; 
       }
    }


    In global.asax

    protected void Application_Start(object sender, EventArgs e)
    {
         Telerik.Sitefinity.Services.SystemManager.ApplicationStart += new EventHandler<EventArgs>(SystemManager_ApplicationStart);
    }
      
    void SystemManager_ApplicationStart(object sender, EventArgs e)
    {
          DCOCustomRoute.RegisterType();
          RouteTable.Routes.Insert(1, new DCOCustomRoute());
    }

    Here is the issue with this code:

    It only works when the destination page was created as a root page.
    Following Example works ok:

    var node = sitemapProvider.FindSiteMapNode("/categories", false, out isAdditional, out pars);

    However we would like to have it in the following format:

    http://www.oursite/directory/categories

    the goal here is: when user hits the folowing url http://www.oursite/directory/categories/fishing it should be routed to http://www.oursite/directory/categories the word fishing is the parameter which we should be able to read in http://www.oursite/directory/categories page. For that purpose we can create asp.net user control register it in sitefinity and drop this control on http://www.oursite/directory/categories page, this user control will extract the parameter from the url. According to received parameter we wold like to display different content from database.

    1) we created in sitefinity page called directory (http://www.oursite/directory).
    2) created page and called it categories, this page was created as a child of directory page (http://www.oursite/directory/categories)
    3) created user control and dropped it on http://www.oursite/directory/categories page.
    changed the followung line of code code in our custom class:

    from:
    var node = sitemapProvider.FindSiteMapNode("/categories", false, out isAdditional, out pars);

    to:

    var node = sitemapProvider.FindSiteMapNode("/directory/categories", false, out isAdditional, out pars);

     

     

    And unfortunately it's not working properly when destination page is a child of the root page. the destination page http://www.oursite/directory/categories is NOT editable anymore in sitefinity administration.

    It only works/editable when the destination page is a root page.

    It is important for as to have the destination page as a child page since we would like to display it in navigation menu under the root in the following format:
    root: Business Directory (url http://www.oursite/directory/)
    child one: By Category (url http://www.oursite/directory/categories)
    child two: By Location (http://www.oursite/directory/locations)

    As I sad the following pages should be able to receive the following urls

    http://www.oursite/directory/categories should receive http://www.oursite/directory/categories/fishing
    and
    http://www.oursite/directory/locations should receive http://www.oursite/directory/locations/stateNameHere

    We will appreciate any help here from your team or sitefinity community.

    And last but not least. If possible I would like to ask sitefinity team to add new feature request in issue tracker for us to vote for making URL Routing much simplier/flexible in sitefinity.

    Thanks.

     

     

  2. Richard Baugh
    Richard Baugh avatar
    201 posts
    Registered:
    22 Aug 2012
    07 May 2012
    Link to this post
    Actually, it is pretty easy to use the Sitefinity routing. If you create a control and add it to a page, you can capture the params and check you data. If your data has a value, you can set a flag to load the page, or you can let the control finish loading and the site will throw a 404.

    What I have done in a control is add a method to the CreateChildControls. This method then gets the string array of parameters sent to the page. Then you can check the parameters against your data. There is no need for adding your own routing. Below is some sample code you can use to get started.

    using Telerik.Sitefinity.Web;
     
    public class YourClass : SimpleView
    {
        protected override CreateChildControls()
        {
            base.CreateChildControls();
            CheckForFilter();
        }
         
        protected void CheckForFilter()
        {
            // Get the parameters sent to the page
            var parms = (string[])this.Page.Request.RequestContext.RouteData.Values["Params"];
            if (parms != null)
            {
                //Check the params against your data here
                foreach(string param in parms)
                {
                    //  If data match call below help
                    //  This is still listed as dperecated, but I have not heard of the new helper
                    //  This will tell sitefinity that this page is were we need to be
                    //  If this is not called, then you will more than likely get a 404
                    RouteHelper.SetUrlParametersResolved(true);
                }
            }
        }
    }

    You can add your other methods and properties to this base control. We have used this for a search control. I am passing my parameters as pages and then parsing them out with the "parms" variable. So you could have a url of http://www.oursite/directory/categories/fishing/bass and through the "parms" you would have an array as follows:
    parms[0] = fishing
    parms[1] = bass

    In your "CheckForFilter" method, you could just loop through the parameters if your url is in a key/value pair type format to try and filter your data:
    for (int i = 0; i < parms.Length; i++)
    {
        switch(params[i])
        {
            //  check for key
            case "fishing":
                i++;
                //  Get Value
                valueVariable = parms[i];
                break;
            //  check for key
            case "state":
                i++;
                //  Get Value
                valueVariable = parms[i];
                break;
        }
    }

    This would allow for a url like the following:
    http://www.yoursite.com/sfpage/sfsubpage/key1/key1value
    So for your example, this same control could be put on the categories and locations page and the following url would be caught:
    http://www.yoursite.com/directory/categories/fishing/bass
    http://www.yoursite.com/directory/locations/state/Maine

    I hope this helps to clear the waters on the Sitefinity URL routing. You just have to figure out your url structure and then you can implement your logic to capture.

    One last note, if you don't care about throwing a 404, you can always set your control to call the helper method, "RouteHelper.SetUrlParametersResolved(true);", and then just show a message from your control if you cannot find any matches in your data. If you setup a structure to your url, they you can also show the search results in a breadcrumb type fashion.
    Answered
  3. vitako
    vitako avatar
    22 posts
    Registered:
    28 Feb 2010
    08 May 2012
    Link to this post

    Hello Richard,

    Thanks for the explanations.

    Looks that in your example you are showing how to access the parameters in a Routed Page

    Can you show me how you register routes in sitefinity?

    For example in asp 4.0 I will do it in global.asax in the following way: 

     

    void Application_Start()
    {
           RegisterRoutes(RouteTable.Routes);
    }
      
    void RegisterRoutes(RouteCollection routes)
    {
           routes.MapRoute(
           "directory-category",   //Name of the Route (can be anything)
           "directory/categories/{category}", // URL with parameters
           "~/directory/category.aspx");  //Page which will handles and process the request
    }

    This route will forward the following url  http://www.mysite.com/directory/categories/fishing to ~/directory/category.aspx page

    And here how I can access URL parameters in a Routed Page

    string categoryName = Page.RouteData.Values["category"].ToString();

  4. Richard Baugh
    Richard Baugh avatar
    201 posts
    Registered:
    22 Aug 2012
    08 May 2012
    Link to this post
    Well that is the nice thing about the Sitefinity routing. Since you would have already created the page ~/directory/categories, Sitefinity will already know that you want to load that page. The routing is already handled by Sitefinity in the SitefinityRoute class. That is why I said it is actually easy to use.

    Do a test. Create a control like I have displayed. You can change the class name to whatever you want. It can be a class control or it can be a web user control, either will work. Then overwrite the CreateChildControls like below and add in a method. In that method, call the RouteHelper.SetUrlParametersResolved(true); helper method first. This will essentially let Sitefinity know that it should load the page. Then grab the parameters using the code I sent you and loop through the values and output them. You will see that the output should be what you are wanting to get.

    In SitefinityRoute, the code will look through the pages that you have create in the admin section. If it can find a page that seems to match a portion of your page request, then it will attempt to load that page and then add the remaining data sent to it to the RouteData.Values["Params"] item. So when you call /directory/categories/fishing, Sitefinity will load the page /directory/categories and then add fishing to the "Params" item. Another example would be /directory/categories/fishing/bass. The value of this.Page.Request.RequestContext.RouteData.Values["Params"] would be fishing, index 0, and bass, index 1, as I mentioned before.

    You really only need to add your own route if you plan to use some url that Sitefinity cannot seem to resolve. Say you wanted to have http://www.yoursite.com/some-url-node/fishing and you wanted that to go to ~/directory/categories/fishing. Or you have some other type of route that Sitefinity cannot interpret, then you would need to add your own route to pick it up. Say instead of using /directory/categories, you want to use /directory-categories but your page is actually created as directory -> categories. This would be an example of needing to register your own route.
  5. Richard Baugh
    Richard Baugh avatar
    201 posts
    Registered:
    22 Aug 2012
    08 May 2012
    Link to this post
    You might refer back to this page also. Further down, Radoslav responds to someone who is trying to use routing in which the page is part of the Sitefinity sitemap. Radoslav's example is for blogs, but you could use the same logic for querying your data. In this example, they are calling GetUrlParameterString. This is a method from and extension class in Sitefinity. It is basically just calling the Page.Request.RequestContext.RouteData.Values["Params"] but instead of getting an array of the params, it builds a concatenated string separated by a forward slash.
  6. vitako
    vitako avatar
    22 posts
    Registered:
    28 Feb 2010
    09 May 2012
    Link to this post
    Thanks for your help Richard.

    It works.
  7. mexner
    mexner avatar
    98 posts
    Registered:
    06 Apr 2006
    05 Jun 2012
    Link to this post
    Great comments and input on this feature; it's helped me a lot. Have you done this before - or considered - modifying the keywords of the page - per dynamic URL? So /directory/categories/fishing/bass would have keywords in the page related to fishing and bass; where as directory/categories/fishing/trout would have different keywords related to trout. This concept can carry forward for the page title, etc....

    Any ideas or examples?
  8. Kristin
    Kristin avatar
    2 posts
    Registered:
    20 Sep 2012
    04 Oct 2012
    Link to this post
    Hi,
    I tried using the code on my page on which I have to get the values from URL
    var parms = (string[])this.Page.Request.RequestContext.RouteData.Values["Params"];
     if (parms != null)
                {
                    foreach (string param in parms)
                    {
                        RouteHelper.SetUrlParametersResolved(true);
                    }
                }
    It works fine if the parameter does not have any special characters in it. Say "Self-Study & TextBox" or "Seld-Study CD/ w Textbox"

    Is there any way to recognize these values on the page.
  9. Darrin Robertson
    Darrin Robertson avatar
    105 posts
    Registered:
    18 Jul 2004
    05 Oct 2012
    Link to this post
    The url will be urlencoded Self & Study will be Self+%26+Study
    You can unencode the string before you do your logic.
  10. Kristin
    Kristin avatar
    2 posts
    Registered:
    20 Sep 2012
    05 Oct 2012
    Link to this post
    Thanks for replying.
    When I encode the word "Self & Study" it gives me an error "A potentially dangerous Request.Path value was detected from the client (&). " I have attached the screenshot of the error.
    What am I doing wrong?
  11. Richard Baugh
    Richard Baugh avatar
    201 posts
    Registered:
    22 Aug 2012
    05 Oct 2012
    Link to this post
    I think the problem is that you are trying to use this as a file path and thus the error. It is best not to use special characters in your actual page url. If you want to use them as querystrings, then you can url encode them.

    I am not sure if the variables you are trying to pass through the url are being used in a database. If they are, you might want to try and strip out the special characters and save the value in your database as a url column. Then when you request a page, you will use this value in the url. You could then lookup your db record based on this stripped url value and then output the data.

    With a querystring, you can url encode the string and pass it as a parameter, but I think you will find more problems if you try to use the string with special characters in the actual url. This is why Sitefinity strips the special characters on pages and titles for modules.
  12. vitako
    vitako avatar
    22 posts
    Registered:
    28 Feb 2010
    06 Oct 2012
    Link to this post
    Thanks to Richard we've got the answer to URL routing a while ago.
    To create our parameters we decided to replace special characters and save it in the database.
    Here some sample that can be used to format the parameters before saving in DB.
     
    public static string FormatRoutingParameter(string param)
    {
        string charsToRemove = @" ~`!@#$%^*()-=+\|[]{};:',<>/";
      
        param = ReplaceCharsInBag(param.Trim(), ".", "");
        param = ReplaceCharsInBag(param.Trim(), "&", "and");
      
        return ReplaceCharsInBag(param, charsToRemove, "_");
    }
      
      
    // Replaces all characters from string s which appear in string bag with new character
    public static string ReplaceCharsInBag(string s, string bag, string newChar)
    {
        int i;
      
        // Search through string bag characters one by one.
        for (i = 0; i < bag.Length; i++)
        {
            // Get current character from the bag.
            string c = bag.Substring(i, 1);
            s = s.Replace(c, newChar);
        }
      
        return s;
    }

    We placed these two methods to our Utilities class and call it as we need.

    Hope it will help you.
12 posts, 1 answered