Copied to clipboard

Flag this post as spam?

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


  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 19:28
    Chriztian Steinmeier
    0

    Using `umbracoRedirect` in conjunction with a UrlProvider doesn't work

    Hi all,

    I have a simple Redirect doctype with the "magic" umbracoRedirect property, so that I can create easy short URLs to content deep within the site.

    It's a Content Picker as described in the docs and it works great.

    Now, I'd like to put all these custom created redirects in a container (preferably within a list view) inside the main site - so the Content structure is this:

    • Content
      • Sample Site
        • Redirects
          • snippets
          • help
        • (all the other content ... )

    The site node (Sample Site) has a Domain set (e.g. example.com)

    The snippets and help documents are created with the Redirect doctype, but as you may have guessed, I'll need to go to example.com/redirects/snippets/ to be redirected to whatever I've specified; what I obviously want to have happen is that example.com/snippets/ triggers the redirect - I just want to have all the Redirect documents in a separate "folder" instead of in the root of the site...

    Enter UrlProvider...

    So I've created a UrlProvider for the Redirect doctype to essentially skip the /redirects/ part of the path, something like this:

    public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode) {
        var content = umbracoContext.ContentCache.GetById(id);
        if (content != null && content.DocumentTypeAlias == "Redirect") {
            // Redirects are stored below a `Redirects` node which shouldn't be part of the URL
            return string.Format("/{0}/", content.UrlName);
        }
    
        return null;
    }
    

    This also works, as I can see on the properties page for a Redirect document, its URL is listed as e.g. "/snippets/". Yay!

    But, alas, when browsing to the /snippets/ URL, I get a standard 404 document and no redirect...

    What am I doing wrong? Do I need to return differently from the GetUrl() method and tell it to "keep handling URLs" or something?

    /Chriztian

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Apr 07, 2016 @ 19:45
    Morten Bock
    0

    I think you also need a ContentFinder as the final part of your puzzle.

    The UrlProvider writes the url, the ContentFinder resolves the url into a node.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 19:57
    Chriztian Steinmeier
    0

    Thanks Morten,

    I was thinking about that but I just figured it "should" work...

    I don't really know the exact order of events in the request pipeline so it's very likely that I'll need to provide my own ContentFinder also, to make it work.

    Luckily, I've just learned about them recently too, so I think I can figure that out - will update with my findings :-)

  • Anders Bjerner 487 posts 2989 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 20:03
    Anders Bjerner
    0

    To follow up on Morten's answer, your content finder should look something like:

    using System.Text.RegularExpressions;
    using System.Web;
    using Umbraco.Web;
    using Umbraco.Web.Routing;
    
    public class RedirectContetFinder : IContentFinder {
    
        public bool TryFindContent(PublishedContentRequest request) {
    
            // Get the raw URL (and do a little parsing)
            string rawUrl = HttpContext.Current.Request.RawUrl.Split('?')[0].TrimEnd('/');
    
            // Match the 
            Match match = Regex.Match(rawUrl, "^/([a-z]+)$");
    
            if (match.Success) {
    
                // Grab the "urlName" from the REGEX
                string urlName = match.Groups[1].Value;
    
                // Find the node in the content cache
                request.PublishedContent = UmbracoContext.Current.ContentCache.GetByRoute("/redirects/" + urlName + "/");
    
    
    
            }
    
            // Return TRUE if we did find a matching node
            return request.PublishedContent != null;
    
        }
    
    }
    

    And then register it just before Umbraco's ContentFinderByNotFoundHandlers:

    ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNotFoundHandlers, RedirectContetFinder>();
    

    Disclaimer: The code is just a quick write some memory. It compiles in Visual Studio, but I haven't tested it further than that.

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Apr 07, 2016 @ 20:12
  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 20:32
    Chriztian Steinmeier
    0

    Hi all - thanks for chippin' in!

    I may be thinking too much about this, but here's how I thought it all worked:

    • User saves document
    • Umbraco calls UrlProviders to generate document's URL
    • URL is stored for the document and viewable on the Properties tab

    (time passes)

    • Browser requests URL
    • Umbraco's internal ContentFinders discover that the incoming request matches the aforementioned document's URL
    • Umbraco sees document has umbracoRedirect property and redirects the browser to actual location

    The only thing that would make that break, would be if the URL (as returned by the UrlProvider) isn't actually stored on the document so Umbraco has to look it up every time it renders the document in the back office, right?

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Apr 07, 2016 @ 20:37
    Morten Bock
    100

    And I think that's exactly it. The UrlProvider is called whenever you call node.Url and is not a persisted property. The only thing that lives on the node is its urlName, which does not contain the path of its parents.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 20:48
    Chriztian Steinmeier
    0

    Great - of course, then it makes perfect sense.

    Dang it - a ContentFinder it is then - but I should just need to look through the Redirect nodes and see if a urlName matches...

    Should be possible :)

    Thanks everyone!

    /Chriztian

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Apr 07, 2016 @ 20:52
    Morten Bock
    0

    You could also just add the urlAlias (I thinks its called) to your redirect nodes, and type in the short url you want, and then let the UrlProvider write that url. But the there is yet another property for the editor to fill in (and screw up)

  • Anders Bjerner 487 posts 2989 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 20:41
    Anders Bjerner
    1

    The URL provider is only for providing one or more visual URLs for the node - eg. the URLs shown in the backoffice, or when using the Url property on an IPublishedContent. The URL provider doesn't change the actual URL of the node.

    So you can look at it like this:

    • An IUrlProvider affects the visual URL. Eg. what the user will see in the backoffice. This can be used so the user will see /snippets/ instead of /redirects/snippets/ in the backoffice, bot it won't change the actual URL.

    • An IContentFinder lets you provide a alternate or "virtual" URL for a given node. Eg. so the user can request /snippets/ instead of /redirects/snippets/.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 07, 2016 @ 21:16
    Chriztian Steinmeier
    0

    Good wrap-up of how they work together. Thanks!

  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Apr 08, 2016 @ 09:46
    Douglas Robar
    0

    I realize this isn't the approach Chriztian was after and I'm not contradicting anything said so far. I just want to propose an alternative approach that would be much easier if it meets the site goals.

    As I understand it, there are pages deep in the site that need to have URLs that appear to be at the root of the site. I've had this situation in which marketing sends out emails and postcards with a vanity url such as example.com/sale but the actual url to the promotion might be at example.com/offers/current/april-fools-sale. I definitely don't want to litter the content tree by allowing promotions directly under the site's homepage for all kinds of reasons.

    What I need is a way for content editors to make the pages and promotions where they belong in the content tree but also provide a simple way for them to enter an alternative URL to reach a page instantly.

    Enter the umbracoUrlAlias property.

    In my example, I would add a property to the Promotion document type with:

    • Name: Vanity URL
    • Alias: umbracoUrlAlias
    • Type: textfield
    • Description: [optional] type an additional url this page should answer to

    In fact, you could type multiple url aliases separated with a comma. Such as sale,big-sale,april-fools-sale. You can even provide a full path if you wanted, such as sale,save/big,offers/april. But I don't bother content editors with those details because they always want a single top-level vanity url so why worry them with additional details they won't use.

    Note that if you had a real page at /sale then a page with an umbracoUrlAlias of sale will never be seen. Real pages are served before looking for aliases.

    The beauty of this solution is that you don't need a custom document type nor a redirection picker. Nor any custom code. Nor any IIS redirects. It's all built-in and obvious.

    The down-side of this approach is that you will, in fact, have multiple urls for a single page which might be an SEO issue. It would depend on how it were used. For short-term promotions I'm not bothered by that potential. For a key page on the site it could be more of an issue. That is, if you ever link to the page by its .Url rather than using the .UmbracoUrlAlias property value.

    cheers,
    doug.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Apr 08, 2016 @ 10:03
    Chriztian Steinmeier
    0

    Thanks for your input Doug,

    However much I am on a never-ending quest for "the right solution for every client," where in this case their emphasis is very much on creating a short vanity URL that points to a page somewhere, I think I'll see if I can somehow change their perception this time to more like: Hey could this page be available as /easy-peasy/ when we send out emails?

    And then they'd probably add easypeasy, eesypeesy and maybe even pez too, but they wouldn't have 4 Redirect nodes lying around (though the list view wouldn't mind that of course).

    I think my main gripe with umbracoUrlAlias was always something along the lines of the SEO thing...

    /Chriztian

Please Sign in or register to post replies

Write your reply to:

Draft