We had an interesting request for customisation of URL structure come into the team that we developer a solution for for 15.4. You'll find it documented here but I thought it was useful to expand on the particular use case and how we used this feature to meet it in a blog post.
The requirement came from SEO optimization, where there was an ask to have a particular URL structure, where the path segments for a given page were different based on whether it was a landing or detail page.
To illustrate, they were looking to do something like this:
Software /software Business Software /software/business-software HR Software /software/business/hr-software Payroll Software /software/business/hr/payroll-software Recruitment Software /software/business/hr/recruitment-software
So taking the "HR Software" page, the URL segment would be "hr-software" when rendering the page itself. But for child pages, the segment is truncated to just "hr".
URL Providers and Content Finders
Umbraco already has a customisable approach to generated URLs for your content. Often the out of the box behaviour will work just fine, but you can amend it, and you do this via a combination of a URL provider and a content finder. The URL provider is used to generate outbound URLs for your document, whenever you request or render a URL. Once you've made a customisation here, you generally need a content finder that will map the URL to your content for incoming requests.
Path Segments
Umbraco also has the concept of a URL segment provider responsible for generating the path segment for a document that will form part of it's - and it's children's - URL. We ship with a DefaultUrlSegmentProvider that creates a segment based on the name of the page, converting spaces to hyphens. You can register your own custom ones if you want to use different logic.
Up until 15.3 though, only one would be used for a given document. Umbraco would work out the segments for each page, but as soon as one URL provider from the collection returned a result, it would be used and no other providers would be called.
The Problem and Solution
This was a problem for the use case. It was quite possible to create a custom IUrlProvider to generate the URLs as needed for pages. And we could implement a IContentFinder that would look-up the correct document from the URL. This content finder would use the IDocumentUrlService to find a document that matches the router, which in turn will use the path segments to find the content.
With only one segment per document (and culture) though, this wouldn't work. We'd have "hr" or "hr-software" registered, but not both. And hence some URLs wouldn't be possible to route.
We resolved this with some amends to the IUrlSegmentProvider interface and behavour when iterating over the collection of URL segment providers to gather all the document segments. AllowAdditionalSegments is a new property that defaults to true to preserve existing behaviour. But if set to false it will no longer "terminate" the segment collation, rather it will allow other URL segment providers to be called and hence allow for more than one segment per document.
Code Sample
Putting that together we can meet the use case with the following code. Firstly the URL provider. This inherits the default provider and passes through to it for all but the "software" pages we want to amend the behaviour for. For those, we look up the ancestors of the current node and amend the path segments to remove the "-software" suffix. Then the content finder. Again, it only applies for "software" pages, identified as being under the "/software/" path. We look up the page via the IDocumentUrlService that will find a document by route, via looking up the registered segments. There's a small check to ensure that the invalid route of e.g./software/business/hr-software/payroll-software
will be rejected (as once we have multiple segments registered, this route will be resolved to a document.
And then we have the URL segment provider. For "software" pages this delegates to the default URL segment provider and truncates the "-software" part of the segment that the default one creates to e.g. "hr". Note that AllowAdditionalSegments is set to true. This will allow the default URL segment provider to be called afterward and register the default segment too (e.g. "hr-software").
There's a small helper method used in the above methods, just used to identify the "software" pages.
And finally a composer to register these three components with Umbraco.
I'm sure there's many more use cases that can be achieved with this combination of URL provider, content finders and now segment providers that can support multiple segments per document and culture.
Comments
Post a Comment