Copied to clipboard

Flag this post as spam?

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


  • Warren Buckley 2054 posts 4305 karma points admin mvp hq c-trib
    May 13, 2015 @ 12:01
    Warren Buckley
    0

    IIS Rewrite or ContentFinder & UrlSegments?

    Hello all
    I am building an application for a client where user generated/submitted questions stored in a custom DB table which have their own IDs.

    Each item needs a unique URL for deep linking so that it can be shared on sites like FaceBook & Twitter.

    The format of the URL needs to be in the following, where 1234 is the unique ID to the posted question
    http://site.co.uk/ask-question/1234

    This page has a route hijack currently and a custom render model view to add some extra items to the Razor view which it is grabbing from some other places in Umbraco.

    I am wondering if to do a native IIS < rewrite > in the web.config or to use Umbraco ContentFinders. What do you think would be the best approach?

    Thanks,
    Warren

  • James Jackson-South 410 posts 1463 karma points c-trib
    May 13, 2015 @ 12:25
    James Jackson-South
    9

    I'm assuming it's a recent v7 install so if I'm way off mark, my apologies.

    These are all virtual nodes tied to a single parent node yeah?

    Then neither.. Personally I would establish a custom route and use an implementation of UmbracoVirtualNodeRouteHandler to find my content.

    This gives you distinct advantage of being able to use Url.Action() in your views to provide consistent navigation links. You'll be also thankful of that when it comes to rendering an XML sitemap.

    If you haven't used UmbracoVirtualNodeRouteHandler before, it's ace. Basically you can do the following..

    In your ApplicationEventHandler implementation:

    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        RouteTable.Routes.MapUmbracoRoute(
        "AskQuestionPage",
        "Ask-Question/{id}/",
         new
         {
             controller = "QuestionPage",
             action = "AskQuestionPage"
         },
         new AskQuestionVirtualNodeRouteHandler(),
         new { id = @"(\d)+" });
    
    }
    

    In your UmbracoVirtualNodeRouteHandler implementation:

    public class AskQuestionVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
    {
    
        protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
        {
            UmbracoHelper helper = new UmbracoHelper(umbracoContext);
            string alias = "QuestionPage"; // This could be derived from a strong typed model.
            string templateAlias = "AskQuestionPage";
    
            return helper.TypedContentAtRoot()
                .First() // Home.
                .Descendants()
                .First(d => d.DocumentTypeAlias.InvariantEquals(alias)
                         && d.GetTemplateAlias().InvariantEquals(templateAlias));
        }
    }
    

    In your controller.

    public class QuestionPageController : RenderMvcController
    {
        public ActionResult AskQuestionPage(RenderModel model, int id)
        {
            // Route hijack as normal.
            // The RenderModel will be the node that you identified in your implementation.
        }
    }
    

    I find this pattern allows me to think much more in a pure MVC format wchich provides me with the deep linking functionality that I want.

    Hopefully this is useful.

  • Stephen 702 posts 1974 karma points core hq
    May 13, 2015 @ 13:31
    Stephen
    3

    If a simple IIS rewriting rule can handle it... why make it more complicated?

    The less code there is, the better.

  • James Jackson-South 410 posts 1463 karma points c-trib
    May 13, 2015 @ 14:06
    James Jackson-South
    0

    Because with IIS rewriting you lose the ability to standardise your navigation links. Generally I'd agree that less is better but in this instance the benefit of being able to use upgradable redirects and action links far outweighs the time spent writing the initial code.

  • MrFlo 123 posts 268 karma points
    Apr 01, 2016 @ 16:27
    MrFlo
    0

    What did you use Warren ?

  • Nicholas Westby 1403 posts 4485 karma points c-trib
    Apr 01, 2016 @ 17:43
    Nicholas Westby
    0

    In order to support URL's like site.com/en-us/contact-us, I have historically used a rewrite rule in UrlRewriting.config. However, I have found that on Umbraco 7.4.1, I could not for the life of me get my 500 error page working, as it seems HttpContext.Current.Server.TransferRequest("~/unhandled-error") would not hit my route (that returns a static HTML file). Once I converted to a content finder, I was then able to get TransferRequest working. If you are curious, here is my content finder implementation:

    // Namespaces.
    using System.Text.RegularExpressions;
    using Umbraco.Core;
    using Umbraco.Core.Models;
    using Umbraco.Web.Routing;
    
    
    /// <summary>
    /// Provides an implementation of <see cref="IContentFinder"/> that handles international URL's.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Handles <c>/en-us/foo/bar</c> where <c>/foo/bar</c> is the nice url of a document
    /// and <c>en-us</c> is the language.
    /// </para>
    /// <para>
    /// This is based on: https://github.com/umbraco/Umbraco-CMS/blob/d50e49ad37fd5ca7bad2fd6e8fc994f3408ae70c/src/Umbraco.Web/Routing/ContentFinderByNiceUrl.cs
    /// </para>
    /// </remarks>
    public class InternationalContentFinder : IContentFinder
    {
    
        #region Variables
    
        private static Regex InternationalUrlRegex = new Regex(@"^/?[a-z]{2}-[a-z]{2}(?=$|/|\?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    
        #endregion
    
    
        #region Methods
    
        /// <summary>
        /// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
        /// </summary>
        /// <param name="docRequest">The <c>PublishedContentRequest</c>.</param>
        /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
        public virtual bool TryFindContent(PublishedContentRequest docRequest)
        {
            var route = default(string);
            if (docRequest.HasDomain)
            {
                var rootId = docRequest.UmbracoDomain.RootContentId.Value.ToString();
                var path = TrimLanguage(docRequest.Uri.GetAbsolutePathDecoded());
                var domain = docRequest.DomainUri;
                route = rootId + DomainHelper.PathRelativeToDomain(domain, path);
            }
            else
            {
                route = TrimLanguage(docRequest.Uri.GetAbsolutePathDecoded());
            }
            var node = FindContent(docRequest, route);
            return node != null;
        }
    
    
        /// <summary>
        /// Removes the language from the beginning of the URL.
        /// </summary>
        /// <param name="url">
        /// The URL.
        /// </param>
        /// <returns>
        /// The URL without the language.
        /// </returns>
        private string TrimLanguage(string url)
        {
            if (InternationalUrlRegex.IsMatch(url))
            {
                var newUrl = InternationalUrlRegex.Replace(url, string.Empty);
                if (string.IsNullOrWhiteSpace(newUrl))
                {
                    newUrl = "/";
                }
                else if (url.StartsWith("?"))
                {
                    newUrl = "/" + newUrl;
                }
                return newUrl;
            }
            else
            {
                return url;
            }
        }
    
    
        /// <summary>
        /// Tries to find an Umbraco document for a <c>PublishedContentRequest</c> and a route.
        /// </summary>
        /// <param name="docreq">The document request.</param>
        /// <param name="route">The route.</param>
        /// <returns>The document node, or null.</returns>
        protected IPublishedContent FindContent(PublishedContentRequest docreq, string route)
        {
            var node = docreq.RoutingContext.UmbracoContext.ContentCache.GetByRoute(route);
            if (node != null)
            {
                docreq.PublishedContent = node;
            }
            return node;
        }
    
        #endregion
    
    }
    
Please Sign in or register to post replies

Write your reply to:

Draft