November Happy Hour will be moved to Thursday December 5th.

Wallmark
Jun 1, 2022
  5198
(3 votes)

Create a Custom 404 Error Page in CMS 12

This will be my first post, rushed it out when I saw a fellow developer asked about it. Cheers!

Intro

While errors can be frustrating for website visitors, they can also provide valuable insights into how the website is being used and what needs to be improved. Error pages can be used to track down broken links, identify missing content, and diagnose other issues.

Optimizely can be used to create custom error pages that can help website owners troubleshoot issues and improve the overall user experience. This article will explain how to create a custom 404 error page using Optimizely.

Handling Exceptions in ASP.NET Core

In order to get ASP.NET Core to handle exception pages, the configuration part of the application needs to setup `UseExceptionHandler`. It is important to remember to use a condition for the development environment when developing, so that the developer exception page is still accessible.

Startup.cs : Configure

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/statuscode/500");
}

app.UseStatusCodePagesWithReExecute("/statuscode/{0}");

The `UseStatusCodePagesWithReExecute` middleware returns the original status code to the client, and generate the response body by re-executing the request pipeline using an alternate path. This means that the URL in the address bar will be the same as what the browser requested.

Status code controller

The fact that the middleware is re-executing the original request means that the MVC controller logic is invoked again. This could potentially cause another error to occur, if, for example, the original request was annotated with `[HttpGet]`.

To avoid this problem, do not restrict your error handling endpoint to a specific set of HTTP methods.

public class StatusCodeController : Controller
{
    private readonly IContentLoader _contentLoader;

    public StatusCodeController(
        IContentLoader contentLoader)
    {
        _contentLoader = contentLoader;
    }

    [Route("/statuscode/{statusCode}")]
    public IActionResult Index(string statusCode)
    {
        var statusCodePage = _contentLoader.GetBySegment(ContentReference.StartPage, statusCode, new LoaderOptions());

        if (statusCodePage is null)
        {
            return View(statusCode);
        }

        return new FilterContentForVisitor().ShouldFilter(statusCodePage)
            ? View(statusCode)
            : View(statusCodePage.GetOriginalType().Name, statusCodePage);
    }
}

The conversion here is that every status code re re-executed against `StatusCodeController`. It takes `statusCode` as a parameter. We will look for a content page in Optimizely under the start page with the name of the status code.

If we find a the page we check that it is actually published and ready to be displayed. Pass the content to a view that handles the content model. Otherwise we will fallback to a more "static" razor page `{statusCode}.cshtml`. 

In this senario it is based on feature folder structure:

ErrorHandling/
├─ 404.cshtml
├─ 500.cshtml
├─ NotFoundPage.cs
├─ NotFoundPage.cshtml
├─ NotFoundPageController.cs
├─ StatusCodeController.cs

404, Not found page

`NotFoundPage` is a page type that is used when a user tries to access a page that does not exist. This page type can be customized to include any desired properties. Some tips for customization include overriding the `PageName` to the status code so that the page gets the right name, disabling `VisibleInMenu`, and also making it non-indexable for Search and Navigation. This can for example be done by creating an `InitalizationModule` and register a conversion in  `ContextIndexer`.

[ContentType(
    GUID = "6391362b-8e66-4e98-82ac-60380301fc04",
    GroupName = SystemTabNames.Content,
    DisplayName = "Not found page",
    Description = "Page to display when a 404 error message occurs."
)]
public class NotFoundPage : PageData
{
    [CultureSpecific]
    [Display(Order = 100)]
    public virtual string Title { get; set; } = string.Empty;

    [CultureSpecific]
    [Display(Order = 200)]
    public virtual string Preamble { get; set; } = string.Empty;

    [CultureSpecific]
    [Display(Order = 300)]
    public virtual XhtmlString MainBody { get; set; } = new XhtmlString();

    [CultureSpecific]
    [Display(Order = 400)]
    public virtual ContentArea MainContentArea { get; set; } = new ContentArea();

    public override void SetDefaultValues(ContentType contentType)
    {
        base.SetDefaultValues(contentType);
        PageName = "404";
        VisibleInMenu = false;
    }
}

Create a `PageController` to handle the `NotFoundPage`. 

public class NotFoundPageController : PageController<NotFoundPage>
{
    public IActionResult Index(NotFoundPage currentContent)
    {
        var isLoggedIn = HttpContext.User.Identity?.IsAuthenticated ?? false;

        return isLoggedIn
            ? View("NotFoundPage", currentContent)
            : NotFound();
    }
}

We check to see if the user is authenticated so that we can display a `200 response` to be able to edit it as an editor. A regular user should get `404 response`. This part can be modified to check on roles instead if needed.

If the user is not authenticated, we pass the request to `NotFound` method and it will be handled by the regular error handling process we registered before.

Summary

This article explains how to create a custom 404 page using Optimizely. This allows for different error pages on different sites and languages. The page can be customized to your liking. Once you are happy you can publish it and share it with your visitors.

Links

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0

Jun 01, 2022

Comments

Mariano Rosiello
Mariano Rosiello Oct 26, 2023 08:54 PM

Hi Jonathan, thanks for the article!

We had this approach working for our custom 404 page, but it stopped working after insatlling the Geta 404 Handler.

<PackageReference Include="Geta.NotFoundHandler.Optimizely" Version="5.0.8" />

Based on the plugin's documentation, having a custom 404 page should be possible by using any of the NET Core techniques (including the one described in your article):

However, the highlighted sounds a bit confusing since the NotFound hanlder would've hanlded the 404 already, so the custom controller would not be reached, which is what I'm experiencing. I've followed the steps in the documentation, and I'm getting the generic 404 from the browser instead:

Have you tried this approach along with the Geta 404 plugin?

Any help or guidance will be appreciated.

Thanks!

Best,
Mariano.

Valina Eckley
Valina Eckley Sep 23, 2024 04:45 PM

I was having issues getting this to work.  But as soon as I changed the route from "statuscode" to "error" it worked

app.UseStatusCodePagesWithReExecute("/error/{0}");
  [Route("/error/{statusCode}")]
  public IActionResult Index(string statusCode)
  {
      var statusCodePage = _contentLoader.GetBySegment(ContentReference.StartPage, statusCode, new LoaderOptions());

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog