Wildcard page in Sitecore - Using Item resolver

There are several ways of implementing the wildcard pages. A wildcard item in Sitecore is a way to create dynamic URLs that pass data through the URL instead of relying on query string values. The name of a wildcard item must be the wildcard character (*), and it matches any item name on the same level as the wildcard item. For example, if the product name is “product-1”, its URL would be http://domain/products/product-1.

So directly coming to the point. I personally used 2 ways of handling the wildcard pages.

  1. Using item resolver, or
  2. Using rendering data source resolver.

Using item resolver


In this, you create a wildcard item as:

- Home
    - Countries
                    * (with no rendering on it, only layout will be available)

Now when you request a URL as https://[domain]/countries/india or https://[domain]/countries/usa or https://[domain]/countries/russia, then it should work.

So idea is that we will keep the data items in the global folder as

- Data
    - Countries
        India - (with presentation detail having the controller rendering, e.g., forecast rendering)
        USA - (with presentation detail having the controller rendering, e.g., forecast rendering)
        Russia - (with presentation detail having the controller rendering, e.g., forecast rendering)

Now, URL https://[domain]/countries/india maps to the * item, and 
We will write a pipeline that will set the context.item as India (available under the Data/Countries folder). 
Since this item has the controller rendering available, that item will be resolved to present on the front end.

So here is the code

using System;
using System.Linq;
using System.Net;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;

namespace Feature.WeatherForecast.Pipelines
{
    public class WildcardItemResolver : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            if (args.Url.ItemPath.StartsWith("/sitecore/content/POC Site/New site/Home/Country", StringComparison.OrdinalIgnoreCase))
            {
                // Extract wildcard parameter from URL
                string wildcardValue = ExtractWildcardValue(args.HttpContext.Request.Url);

                if (!string.IsNullOrEmpty(wildcardValue))
                {
                    // Resolve the wildcard value to a specific Sitecore item
                    var item = ResolveWildcardItem(args, wildcardValue);
                    if (item != null)
                    {
                        // Set the resolved item in the Sitecore context
                        Context.Item = item;
                    }
                    else
                    {
                        Item errorPage = args.GetItem("/sitecore/content/POC Site/New site/Home/404");
                        if (errorPage != null)
                        {
                            args.ProcessorItem = errorPage;
                            Context.Item = errorPage;
                            Context.Items["httpStatus"] = HttpStatusCode.NotFound;
                            args.HttpContext.Response.TrySkipIisCustomErrors = true;
                        }
                        // Optionally handle when the wildcard value doesn't resolve to any item
                        Log.Warn($"Wildcard item not found for value '{wildcardValue}'", this);
                    }
                }
            }
        }

        private string ExtractWildcardValue(Uri url)
        {
            // Extract wildcard value from the URL path
            return url.Segments.Last();
        }

        private Item ResolveWildcardItem(HttpRequestArgs args, string wildcardValue)
        {
            // Implement logic to resolve wildcard value to a specific Sitecore item
            var wildcardFolderPath = "/sitecore/content/POC Site/New site/Data/Countries";
            return args.GetItem($"{wildcardFolderPath}/{wildcardValue}");            
        }
    }
}

Finally patch your changes as:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="Feature.WeatherForecast.Pipelines.WildcardItemResolver, Feature.WeatherForecast" />
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

If it doesn't work after this then reason can be following:

<mvc.getPageItem patch:source="Sitecore.Mvc.config">
  <processor type="Sitecore.Mvc.Pipelines.Response.GetPageItem.SetLanguage, Sitecore.Mvc"/>
  <processor type="Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromRouteValue, Sitecore.Mvc"/>
  <processor type="Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromRouteUrl, Sitecore.Mvc"/>
  <processor type="Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc"/>
  <processor type="Sitecore.ContentTesting.Mvc.Pipelines.Response.GetPageItem.PageLevelTestVariantResolver, Sitecore.ContentTesting.Mvc" patch:source="Sitecore.ContentTesting.Mvc.config"/>
  <processor type="Sitecore.ContentTesting.Mvc.Pipelines.Response.GetPageItem.ContentTestVariantResolver, Sitecore.ContentTesting.Mvc" patch:source="Sitecore.ContentTesting.Mvc.config"/>
</mvc.getPageItem>

The processor GetFromOldContext will set the PageItem to Context.Item only if the previous processors haven't already identified an item.

Recommedation

Create a processor for this pipeline that comes after either GetFromRouteUrl or GetFromOldContext to determine whether you want to continue using the Page Item found, or use from the Custom Item Resolver.

Or

The really simple solution to resolve this issue is to reorder the processors within the mvc.getPageItem pipeline so that GetFromOldContext always comes after the SetLanguage processor.

<sitecore>
  <pipelines>
    <mvc.getPageItem>
      <processor type="Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc">
        <patch:delete />
      </processor>
      <processor patch:after="processor\[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.SetLanguage, Sitecore.Mvc'\]" type="Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc"/>
    </mvc.getPageItem>
  </pipelines>
</sitecore>

A little more involved solution is to override the PageContext to fix the issue; to do that we need to override the SetupPageContext processor to add our custom PageContext

namespace YourNamespace
{
    using System.Web.Routing;
    using Sitecore.Data.Items;
    using Sitecore.Mvc.Pipelines.Request.RequestBegin;
    using Sitecore.Mvc.Presentation;

    public class SetupPageContext : Sitecore.Mvc.Pipelines.Request.RequestBegin.SetupPageContext
    {
        protected override PageContext CreateInstance(RequestContext requestContext, RequestBeginArgs args)
        {
            return new PageContextFixed
            {
                RequestContext = requestContext
            };
        }
    }

    public class PageContextFixed : PageContext
    {
        protected override Item GetItem()
        {
            // Assumption: If you have a Context.Item you have to have a Context.Language
            return Sitecore.Context.Item ?? base.GetItem();
        }
    }
}

And the configuration:

<sitecore>
  <pipelines>
    <mvc.requestBegin>
      <processor patch:instead="processor\[@type='Sitecore.Mvc.Pipelines.Request.RequestBegin.SetupPageContext, Sitecore.Mvc'\]" type="Example.SetupPageContext,Example" />
    </mvc.requestBegin>
  </pipelines>
</sitecore>


The information credit goes to the following:

Now let's understand the same scenario for wildcard page with datasource resolver.



1 comment:

  1. https://sitecorepeanuts.blogspot.com/2024/03/wildcard-page-in-sitecore.html

    ReplyDelete

Creating Solr core for sitecore using command prompt

We setup the solr cores using command “C:\solr8985\sc103solr-8.11.2\bin>solr.cmd create -c sitecore_sxa_web_index -d sitecore_configset” ...