One of the most common customizations that I have seen in Sitecore is the addition of custom processors in the httpRequestBegin
pipeline, usually to add is some custom logic to resolve the context item, maybe to deal with custom URLs or wildcard items. Since this pipeline runs for every single request, there are plenty of reasons to customize here.
Most often I’ve seen , you plug in after the Sitecore.Pipelines.HttpRequest.ItemResolver
processor with whatever your custom requirements are. However, if you are using Sitecore MVC (and I hope you are) then you may find that your custom logic has not been applied and the Sitecore.Context.Item has been reset back to default Sitecore logic.
This has come up a number of times on Slack and caught a few colleagues out. It also caught me out a while back when I was doing some wildcard work with MVC:
Where did you lose your Context Item?
With Sitecore MVC a number of additional pipelines were introduced that were only run specifically for MVC requests.
But does this affect the Context Item?
The GetItem()
method in the Sitecore.Mvc.Presentation.PageContext
class in Sitecore.MVC.dll
runs the mvc.getPageItem
pipeline, which has a number of processors defined. One of those is Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromRouteUrl
which resolves the Item (again) from the Route URL.
When the pipeline returns, a number of checks are made, one of which is to check if the Context.Item.ID
matches the Item returned, if not it sets Context.Item to the one resolved by the mvc.getPageItem
pipeline.
You can see the logic in Sitecore.Mvc.Presentation.PageContext, Sitecore.MVC
:
protected virtual Item GetItem() { using (new RecursionPreventer("GetItem", this.getItemFlag)) { using (TraceBlock.Start(Sitecore.StringExtensions.StringExtensions.FormatWith("Get item for {0}.", (object)typeof(PageContext)))) { Item obj = PipelineService.Get().RunPipeline<GetPageItemArgs, Item>("mvc.getPageItem", new GetPageItemArgs(), (Func<GetPageItemArgs, Item>)(args => args.Result)); if (obj != null) { if (Context.Language == (Language)null || Context.Language != obj.Language) Context.Language = obj.Language; if (Context.Item == null || Context.Item.ID != obj.ID) Context.Item = obj; return obj; } Tracer.Info((object)"Using Sitecore.Context.Item."); return Context.Item; } } }
How to get your Context Item back
Update the code in your custom ItemResolver in the httpRequestBegin
pipeline and store a boolean flag to indicate to indicate that the item has already been resolved using custom logic, this saves having to run the resolving logic twice:
public class CustomItemResolver : HttpRequestProcessor { public override void Process(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); if (Sitecore.Context.Item == null || Sitecore.Context.Item.TemplateID != {guid}) return; Sitecore.Context.Item = ResolveCustomItem(args.Url); Sitecore.Context.Items["custom::ItemResolved"] = true; } }
Create another class that checks if the item has previously been resolved in the regular httpRequestPipeline
and if so sets the result to the context item:
public class CheckItemResolved : MvcPipelineProcessor<GetPageItemArgs> { public override void Process(GetPageItemArgs args) { var resolved = Sitecore.Context.Items["custom::ItemResolved"]; if (MainUtil.GetBool(resolved, false)) { // item has previously been resolved args.Result = Sitecore.Context.Item; } } }
And then patch it in before the MVC pipeline that resolves the item again from the Route URL.
<pipelines> <mvc.getPageItem> <processor type="MyProject.Custom.Pipelines.CheckItemResolved, MyProject.Custom" patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromRouteUrl, Sitecore.Mvc']" /> </mvc.getPageItem> </pipelines>
The advantage of setting the result item to the Context Item and patching it before the default MVC GetFromFromUrl()
pipeline is the code breaks out early so it doesn’t go through a whole bunch of logic to resolve the item only for you to go and reset it again.
Now the Context.Item
should be what you are expecting. Although this “reset” is important, you probably don’t want to be using Context.Item in MVC. Take a read of Sitecore MVC Item Maze post by Pavel Veller, it’s really great detail on the different types of Item properties.
What you probably want is PageContext.Current.Item
(i.e. current page Item) or RenderingContext.Current.Rendering.Item
(i.e. Datasource with fallback) but the above is an important and required step when using custom Item Resolvers.
One comment