Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jakob Kristensen 36 posts 169 karma points
    Jan 05, 2017 @ 21:24
    Jakob Kristensen
    0

    Urlrewriting - which tutrorials is up to date?

    Hello Umbraco people!

    I've recently started looking into UrlRewriting (those seo people huh?), and now i need to create a sort of urlprovider on a certain detailpage which has dynamic content rendered with a querystring ?id= now we want it to be a nice url so we want it to look something like /details/town/house/id.

    I've looked into some material i found on google but have a hard time figuring out what to use.

    Should i do urlrewritingengine, or maybe the urlsegmentprovider can do this ?

    This could work but would add extra pages would woudlnt be found and be bad for seo. http://wondergrub.com/articles/an-introduction-to-umbraco-urlrewriting/

    Then i can implement my own provider like so seems abit excessive. http://24days.in/umbraco-cms/2014/urlprovider-and-contentfinder/

    Then there is the official UrlSegmentprovider which looks like the route i would go but the docs look rather old so not sure if it will work in UMB7.5 ? https://our.umbraco.org/documentation/reference/routing/request-pipeline/outbound-pipeline

    Is there any other ways to do this ?

    Please share your thoughts :)

    Cheers

  • Marc Goodson 2141 posts 14344 karma points MVP 8x c-trib
    Jan 06, 2017 @ 19:51
    Marc Goodson
    100

    Hi Jakob

    You could always set a Canonical Url to avoid being penalised for multiple urls to the same content: https://support.google.com/webmasters/answer/139066?hl=en

    Is the data for each house stored in Umbraco ? with a nicer Url ?

    if so I would probably implement an IContentFinder to look for incoming requests in the format /Details/Town/House, I'd use the Id or the path to locate a corresponding house in Umbraco (by storing the Id/Path as a property in Umbraco), if one was found I'd 301 redirect to the house details with the nice url:

     public class FindContentByHouseId : IContentFinder
        {
            public bool TryFindContent(PublishedContentRequest contentRequest)
            {
                // return false as soon as we realise there is no match.
                 var path = contentRequest.Uri.AbsolutePath;
                // lets see if this path is specified in the houseUrl field on any of our 'house pages' content items in the cache
                var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
                var matchingContent = umbracoHelper.TypedContentSingleAtXPath("//HousePage[houseUrl/text() = '" + path + "']");
                if (matchingContent == null)
                {
                    return false;
                } 
    
                contentRequest.SetRedirectPermanent(matchingContent.Url);
                    // tell the umbraco pipeline we've found the content, no need to process any other rules
                    return true;
            }
        }
    

    If the data for the house resides outside of Umbraco then you could map a custom MVC route for your Url through Umbraco to a Custom RenderMvcController

    RouteTable.Routes.MapUmbracoRoute(
          "House details",
          "Details/Town/House/{id}",
          new
           {
                controller = "Housing",
                action = "Details",
                id = UrlParameter.Optional  
     }, 
    new UmbracoVirtualNodeByIdRouteHandler(1234) );
    

    You could then create a HousingController that inherits from RenderMvcController, with a Details Action to handle the request

     public class HousingController : RenderMvcController
        {
            public ActionResult Details(RenderModel model, int id)
            {
    // use the Id to retrieve the house data from the external source
    // create a Custom View Model that inherits RenderModel
    // pass this to your view
                var houseInfo = HouseService.GetById(id);
                var vm = new HouseViewModel(model.Content, houseInfo);
                return View(vm);
             }
    }
    

    You may be wondering where the model.content comes from... this will contain the IPublishedContent from Umbraco for the node 1234, specified in the VirtualNodeRouteHandler in the Custom mapped route:

    UmbracoVirtualNodeByIdRouteHandler(1234)

    This gives your external data an 'umbraco context' so if your templates have macros/functionality that builds for example a breadcrumb or similar out of the Umbraco content tree, they will still work, even though your house info is not stored there...

    You can implement your own logic for finding an appropriate Umbraco page for context purposes if you wish, by creating your own VirtualNodeRouteHandler

    eg

     public class FindHousingContextRouteHandler : UmbracoVirtualNodeRouteHandler
        {
            protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
            {
     var umbracoHelper = new UmbracoHelper(umbracoContext);
    // find an IPublishedContent item of your choosing, or indeed construct your own Virtual IPublishedContent item...
    }
    }
    

    More info here: http://shazwazza.com/post/custom-mvc-routes-within-the-umbraco-pipeline/

    regards

    Marc

  • Jakob Kristensen 36 posts 169 karma points
    Jan 09, 2017 @ 20:19
    Jakob Kristensen
    0

    We ended up going the same route as you described after finding out about virtualnodes, your examples really helped figuring out how they worked :)

    To add to the code above for future readers, that might be abit confusing was for me atleast.

      public class HousingViewModel : Umbraco.Web.Models.RenderModel
    {
        public HousingViewModel(IPublishedContent content, MyClass house)
            : base(content) {
            this.house= house;
    
        }
        public MyClass house{ get; set; }
    
    }
    

    This made the render model work and i could pass my custom class to the razor template and pickup on my other sections on my layoutpage.

    @using WHEREIKEEPMYMODELS;
    @inherits Umbraco.Web.Mvc.UmbracoViewPage<HousingViewModel>
    

    This would make sure i could make use of model and have intellisense to call it with ex. model.ID, really slick!

    I do have a follow up question to wrap this up, if working with custom data, how would you construct the link in the frontend ?

    Put it in a link in your external data viewmodel, and encode /type/town/id save it as a niceurl and when iterating use that to call the link directly. (this would work but i see some work being put in to create these correct with encoding, special chars etc.)

    Or can you wire it up in umbraco so you could get it through the page context and simply iterate the parent? (and in the same go wireup the sitemap to content maybe?)

    Thanks alot for your elaborate answers i very much appreciate the work you put in!

    Jakob

  • Marc Goodson 2141 posts 14344 karma points MVP 8x c-trib
    Jan 15, 2017 @ 12:06
    Marc Goodson
    1

    Hi Jakob

    Glad it helped as an approach, I prefer this way when the data in external, but it's a lot to explain all the way through :-)

    it's hard to tell from your description above whether Town's also come via an external db, and so are another Custom Route, that has a model that contains all the towns houses, or whether Town's are an Umbraco page and you use a Surface controller to list out pages of houses.

    Either way though

    You should be able to construct links using

    Url.Action("Details","Housing", new {id= YourHouseId});

    if you know the Id of the house.

  • Jakob Kristensen 36 posts 169 karma points
    Jan 24, 2017 @ 00:30
    Jakob Kristensen
    0

    That did it, marked correct answers for future readers, and added high fives, thanks alot for the help :)

Please Sign in or register to post replies

Write your reply to:

Draft