allBlogsList

Handling QueryString in Custom Item Resolver processor MVC

On a recent project I was tasked with writing a custom item resolver in Sitecore. The goal was to translate http://domain/blog/tag/tag-name to http://domain/blog?tag=tag name.

Since I had done this a before, I quickly wrote the following code

public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (((Context.Item == null) && (Context.Database != null)) && !string.IsNullOrWhiteSpace(args.Url.ItemPath))
            {
                Profiler.StartOperation("Custom resolver");
                try
                {
                    string qsValue = string.Empty;
                    string decodedItemName = MainUtil.DecodeName(args.Url.ItemPath);
                    string blogItemPath = ResolveBlogPath(decodedItemName, out qsValue);
                    Item blogItem = args.GetItem(blogItemPath);
                    if (blogItem != null)
                    {
                        Context.Item = blogItem;
                        NameValueCollection queryStringCollection =
                            StringUtil.ParseNameValueCollection(args.Url.QueryString, '&', '=');
                        queryStringCollection.Add("tag", qsValue);

                        args.Url.QueryString = StringUtil.NameValuesToString(queryStringCollection, "&");
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("could not resolve url", ex, this);
                }
                Profiler.EndOperation();
            }
        }
        protected string ResolveBlogPath(string decodedItemName, out string qsValue)
        {
            qsValue = string.Empty;
            try
            {
                Match match = Regex.Match(StringUtil.EnsurePostfix('/', decodedItemName), @"(^.+/tag/)(.+)/$", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    qsValue = WebUtil.UrlEncode(match.Groups[2].Value);
                    return match.Groups[1].Value;
                }
            }
            catch (Exception ex)
            {
                Log.Error("could not resolve url", ex, this);
            }
            return string.Empty;
        }
    }

Next, I added my custom processor to the httpRequestBegin pipeline

 <httpRequestBegin>
<processor patch:after="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="XCore.SitecoreExtensions.Pipelines.HttpRequestBegin.BlogItemResolver, XCore.SitecoreExtensions" />
 </httpRequestBegin>

The code worked fine for all WebForm pages but it did not work for any item that had MVC layout..

After some investigation and a little help from Sitecore support, I found out that Sitecore traditionally has been rewriting URL in the ExecuteRequest processor step.

        <processor type="Sitecore.Mvc.Pipelines.HttpRequest.TransferRoutedRequest, Sitecore.Mvc" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" />

Here is the magic code:
 args.Context.RewritePath(filePath, args.Context.Request.PathInfo, args.Url.QueryString, false);

This processor is never executed on an MVC solution as Sitecore.Mvc.config adds a new processor “TransferRoutedRequest” immediately after ItemResolver that routes all MVC requests to the appropriate controller, and aborts the pipeline.

The Fix

The fix is simple, all I had to do was

replace the following line:
args.Url.QueryString = StringUtil.NameValuesToString(queryStringCollection, "&");
With this line:
args.Context.RewritePath(blogItemPath, blogItem.Paths.FullPath, StringUtil.NameValuesToString(queryStringCollection, "&"));

Hope this helps you. You can read more about RewritePath on MSDN - http://msdn.microsoft.com/en-us/library/system.web.httpcontext.rewritepath(v=vs.110).aspx