The high-level overview looks something like this:
- Have a 404 page item under your SiteRoot, with a rendering control that uses code to properly set a 404 status code.
 - Have a 404 processor in the 
httpRequestBeginpipeline to override Sitecore's default 404 functionality, so that clients aren't redirected and the requested URL is preserved. - Have a 404 processor in the 
mvc.getPageItempipeline to force URL's that map to items to return a 404 when appropriate. 
Do make sure to configure the 
ItemNotFoundURL setting to the 404 page item URL within your site.
Web.config
Set the existingResponse attribute to "Auto" to give your website control over how status codes get handled. The "Auto" mode allows you to set 
Response.TrySkipIisCustomErrors to true to bypass the default IIS 404 page, or anything that's been defined in the httpErrors configuration node. errorMode="DetailedLocalOnly" existingResponse="Auto">
 
  statusCode="500" path="/static/errors/500.html" responseMode="File"/>    
  
Setting the Status Code with a 404 Rendering Control
We need to either have a Controller Rendering or a View Rendering that has code to set a few different properties to allow us to properly return a 404 status code.
There are two critical properties that must be set in your rendering: one obviously being the 
Response.StatusCode to 404, and the other to set Response.TrySkipIisCustomErrors to true. Remember, TrySkipIisCustomErrors will cause IIS to ignore the configuration in the httpErrorsconfiguration node since we've set the existingResponse attribute on that node to "Auto".public ActionResult Request404Page()
{
   HttpContext.Current.Response.StatusCode = 404;
   HttpContext.Current.Response.TrySkipIisCustomErrors = true;
   HttpContext.Current.Response.StatusDescription = "404 File Not Found";
   return View();
}
Create a 404 Processor
Use a custom 
HttpRequestProcessor at the end of the pipeline to handle when a context item has not been resolved. This processor exists only to avoid Sitecore's default functionality of redirecting to the 404 page. Instead of redirecting the client or use a server side only redirect, the goal is to leave the URL alone and avoid causing a messy client experience. This is achieved merely by setting the Context.Item and ProcessorItem properties to your desired 404 page.public class 404HttpRequestProcessor : HttpRequestProcessor
{
    public override void Process(HttpRequestArgs args)
    {
        if (Sitecore.Context.Item != null || Sitecore.Context.Site == null)
        {
            return;
        }
        if (string.Equals(Sitecore.Context.Site.Properties["enableCustomErrors"], "true", StringComparison.OrdinalIgnoreCase) == false)
        {
            return;
        }
        var pageNotFound = this.Get404PageItem();
        requestArgs.ProcessorItem = pageNotFound;
        Sitecore.Context.Item = pageNotFound;
    }
    protected Item Get404PageItem()
    {
        // This is largely up to how the project in general is setup.
        // My solutions are heavily Fortis dependent, but for Vanilla 
        // Sitecore setups you could just use the following setting:
        // Settings.GetSetting("ItemNotFoundUrl", "/errors/404");
        // and pull the Item object from that path
        var path = Sitecore.Context.Site.RootPath + "/" + Settings.GetSetting("ItemNotFoundUrl", "/errors/404");
        var item = Sitecore.Context.Database.GetItem(path);
        return item;
    }   
}
Patch your custom 404 processor, in this example called 
404HttpRequestProcessor, at the end of the httpRequestBegin Pipeline. type="MyProject.Pipelines.HttpRequest.404HttpRequestProcessor, MyProject.Core"
                        patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']">
 
Now we have the following:
- A 404 page within sitecore that we can customize in anyway we want.
 - We avoid redirecting the client, so they don't lose their URL or break the back button.
 - There's no spaghetti setup within IIS that bounces requests back and forth between IIS and Sitecore.
 
But we're not quite done yet!
404'ing URL's That Map to an Item
Unfortunately, the above setup will only work if you type in a URL that does not map to an item in your content tree (e.g. "www.mywebsite.com/page-item/that/does-not-exist"). Even if you set 
Context.Item to null in your httpRequestBegin pipeline, Sitecore will "re-resolve" that URL in the mvc.getPageItem pipeline if that URL happens to map to an item in Sitecore. There are certain situations where you want to return 404 for a URL that technically exists, but if this scenario is not applicable to your project you don't need to include the following processor.
To 404 a URL that technically maps to a content item, we need to add a processor to the 
mvc.getPageItem pipeline. The code will be excruciatingly similar to the 404RequestProcessorprocessor we added in the httpRequestBegin pipeline, we'll just be modifying a few different "sister" properties to properly exit the mvc.getPageItem pipeline. Of course, since both processors are similar it's possible to abstract the code out and avoid duplication, but I'll leave that as an exercise to the reader.
It is assumed at this point that a previous processor in the pipeline doesn't like something about the current request, so that processor set 
args.Result to null.public class 404PageItemProcessor : GetPageItemProcessor
{
    public override void Process(GetPageItemArgs args)
    {
        if (args.Result != null || Sitecore.Context.Site == null)
        {
            return;
        }
        if (string.Equals(Sitecore.Context.Site.Properties["enableCustomErrors"], "true", StringComparison.OrdinalIgnoreCase) == false)
        {
            return;
        }
        var pageNotFound = Get404PageItem();
        pageItemArgs.ProcessorItem = pageNotFound;
        pageItemArgs.Result = pageNotFound;     
        Sitecore.Context.Item = pageNotFound;
   }
   protected Item Get404PageItem()
   {
       // This is largely up to how the project in general is setup.
       // My solutions are heavily Fortis dependent, but for Vanilla 
       // Sitecore setups you could just use the following setting:
       // Settings.GetSetting("ItemNotFoundUrl", "/errors/404");
       // and pull the Item object from that path
       var path = Sitecore.Context.Site.RootPath + "/" + Settings.GetSetting("ItemNotFoundUrl", "/errors/404");
       var item = Sitecore.Context.Database.GetItem(path);
        return item;
    }
}
Please pay attention to the fact that the code above sets two properties on the 
GetPageItemArgsobject: ProcessorItem and Result. Both of these properties must be set to the desired 404 page or Sitecore will simply ignore the property value that you set (for reasons I will not get into in this post).
Now, just add the 
404PageItemProcessor to the end of the mvc.getPageItem pipeline. type="MyComp.Pipelines.GetPageItem.404PageItemProcessor, MyComp.Core" 
patch:after="processor[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc']">
 
No comments:
Post a Comment