1

As per Sitecore's example code for ASP.NET Headless you can have your default controller method look like this:

[UseSitecoreRendering]
public IActionResult Index(Route route) {
    var request = HttpContext.GetSitecoreRenderingContext();

    if (request.Response.HasErrors) {
        foreach (var error in request.Response.Errors) {
            switch (error) {
                case ItemNotFoundSitecoreLayoutServiceClientException notFound:
                    Response.StatusCode = (int)HttpStatusCode.NotFound;
                    return View("NotFound", request.Response.Content.Sitecore.Context);
                case InvalidRequestSitecoreLayoutServiceClientException badRequest:
                case CouldNotContactSitecoreLayoutServiceClientException transportError:
                case InvalidResponseSitecoreLayoutServiceClientException serverError:
                default:
                    throw error;
            }
        }
    }

    return View(route);
}

But I want to display a Sitecore page as the 404 page instead of a regular View inside the ASP.NET rendering host.

Let's say I have a page at the following path in Sitecore: /sitecore/content/MySite/Home/ErrorPages/404

How would I go about rendering that specific page when the rendering host encounters a "NotFound" scenario? This has to work with model binding, of course.

I guess I have to do something instead of Return View("NotFound", request.Response.Content.Sitecore.Context); but what?

2 Answers 2

3

You can use:

return Redirect("/ErrorPages/404");

or

return Redirect("http://myErrorPage");

instead of:

return View("NotFound", request.Response.Content.Sitecore.Context);

Updated:

I don't see OOTB ways if a 404 status code is needed, but as an option, it is possible to use one of the standard approaches like the one below:

  1. Create your own RedirectResultWithStatusCode (it can be inherited from RedirectResult) with the option to pass statusCode

  2. Override the existing RedirectResultExecutor, that is registered:

    services.TryAddSingleton<IActionResultExecutor<RedirectResult>, RedirectResultExecutor>();

And then override ExecuteAsync, so it might look something like that:

public override Task ExecuteAsync(ActionContext context, RedirectResult result)
{
    var redirectResultWithStatusCode = result as RedirectResultWithStatusCode;
    if (redirectResultWithStatusCode == null)
    {
        return base.ExecuteAsync(context, result);
    }

    // My logic is here

    /*
    context.HttpContext.Response.StatusCode = redirectResultWithStatusCode.StatusCode;
    context.HttpContext.Response.Headers[HeaderNames.Location] = result.Url;
    */

    return Task.CompletedTask;
}
                        
3
  • You would need to make sure that the response code is 404 tho
    – Richard Seal
    Commented Sep 7, 2022 at 14:39
  • 1
    The problem with that is that the URL will change. A proper 404 would keep the original URL then show an error page. First doing a 301 and then displaying 404 is not the proper way to handle it. Commented Sep 7, 2022 at 15:36
  • @GTHvidsten, updated the answer to cover 404 StatusCode, but as far as I remember, there are a lot of modules that were developed for Sitecore to cover 404, but I'm not sure they are still applicable for Headless solutions.
    – Alexander
    Commented Sep 8, 2022 at 10:49
2

We have implemented it as a custom Pipeline with Sitecore JSS therefore it will work also with ASP.NET Headless. Of course in integrated mode. In headless mode you need to approach this with different logic on FE / renderer side.

This solution is not redirecting visitors to 404 page. It keeps current URL which is much better.

Idea is to catch when Context.Item equals null with few other conditions so you are sure that Sitecore would show regular 404 error page. Your pipeline will be patched before Sitecore's NotFound pipeline. You will then set Context Item to $site$/Error Pages/404 and set proper HTTP Error code to Not Found.

Code for Processor:

using System.Linq;
using System.Net;
using System.Web;
using System.Web.Routing;
using Sitecore;
using Sitecore.Pipelines.HttpRequest;
using Sitecore.XA.Foundation.SitecoreExtensions.Extensions;

namespace Client.Foundation.Pipelines
{
    public class ItemNotFound : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            using (new PerformanceProfiler(nameof(ItemNotFound)))
            {
                if (Context.Item == null
                && Context.Site != null
                && Context.Database != null

                && string.IsNullOrEmpty(Context.Page.FilePath)
                && RouteTable.Routes.GetRouteData(args.HttpContext) == null
                && !args.PermissionDenied)
                {
                    string[] localPathElements = args.LocalPath.Split('/');

                    string contextItemStartPath = Context.Site.StartPath;

                    Context.Item = Context.Database.GetItem(contextItemStartPath + "/ErrorPages/404");

                    HttpContext.Current.Response.TrySkipIisCustomErrors = true;
                    HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
            }
        }
    }
}

Config entry to introduce processor:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor type="Client.Foundation.Pipelines.ItemNotFound, Client.Foundation"
                           patch:before="processor[@type='Sitecore.XA.Feature.ErrorHandling.Pipelines.HttpRequestBegin.ItemNotFoundResolver, Sitecore.XA.Feature.ErrorHandling']" />
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>
4
  • This definitely looks good when requests are always served by Sitecore instance, is this work for [Headless topology] (not Integrated topology) mentioned here below? doc.sitecore.com/xp/en/developers/hd/190/…
    – Alexander
    Commented Sep 8, 2022 at 15:14
  • 1
    This is what I've been recommended on another forum as well, and is what I'll try to implement. It would be nice to be able to do this directly in the Rendering Host, though, because then I can show Sitecore pages for other error scenarios as well (i.e. rendering host exception showing a Sitecore page saying "something went wrong" but still being able to be edited by Sitecore editors) Commented Sep 8, 2022 at 15:23
  • This is only valid for integrated mode of course / answer updated Commented Sep 9, 2022 at 6:03
  • @PeterProcházka A few other comments. You're referencing Sitecore.XA namespaces and types. I guess this has to do with SXA, which is not installed in my instance. Does that mean that RouteTable.Routes.GetRouteData() is not necessary?. Seeing as you patch your pipeline before Sitecore.XA stuff, where would you place it without SXA? Also, when I'm removing XA dependencies and patch it after Sitecore.Pipelines.HttpRequest.ItemResolver then Context.Item is NULL even for items that exist. I guess I should patch it elsewhere, but I'm not sure which pipeline(s) are in use by LayoutService. Commented Sep 9, 2022 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.