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 ?
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...
}
}
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!
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});
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
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:
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
You could then create a HousingController that inherits from RenderMvcController, with a Details Action to handle the request
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
More info here: http://shazwazza.com/post/custom-mvc-routes-within-the-umbraco-pipeline/
regards
Marc
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.
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.
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
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.
That did it, marked correct answers for future readers, and added high fives, thanks alot for the help :)
is working on a reply...