We use error pages to be shown to the end-user a friendly message and may be email the detailed information about the exception to developers who maintains the website. Some times we may face a situation where error page itself throws some exception, causing redirect loop and application pool goes down.
Error pages usually represents a simple message with the same user experience and colour theme across pages. So, most probably this may also be using one of the templates like other pages. Imagine a situation where our MasterPage is having some unhandled exception, and we redirects user to the Error page from Application_Error in Global.asax. And error page itself throws an exception and again it redirects to the error page, causing redirect loop.
So, we’re going to do following to configure Error pages easily through UI which in any case will never get trapped into redirect loop.
- Step 1: Create a logic to avoid redirect loop.
- Step 2: Add new field to Pages module to mark one of the pages to be shown when exception occurs.
- Step 3: Create a service to update custom field values.
- Step 4: Add link to action menu to consume the service and set any page as Error page.
Step 1
Add new MasterPage which is going to be used by our pages. Place following code to our MasterPage demonstrating an unexpected exception.
Base.Master.cs
protected void Page_Load(object sender, EventArgs e) { object i = "string"; int num = (int)i; // This will throw an unhandled exception }
Add 2 new pages Home and Error and inherit them from our MasterPage. This means both are going to throw exception.
Global.asax.cs
protected void Application_Error(object sender, EventArgs e) { Server.ClearError(); var manager = new PageManager(); var provider = SiteMapBase.GetCurrentProvider(); if (provider == null) return; if (provider.CurrentNode == null) return; var pageId = new Guid(provider.CurrentNode.Key); var currentPageNode = manager.GetPageNode(pageId); if (currentPageNode != null) { var errorPage = App.WorkWith() .Pages() .LocatedIn(PageLocation.Frontend) .ThatArePublished() .Get() .Where(i => i.GetValue<string>("IsErrorPage") == "true") .FirstOrDefault(); if (errorPage != null) { if (errorPage.UrlName == currentPageNode.UrlName) { Response.Write("Woahh, something really bad happened."); return; } else { Response.Redirect(errorPage.GetUrl()); } } else { Response.Write("Oops, something went wrong."); return; } } }
Don’t worry about the highlighted code line. We’ll cover it up in next step.
Step 2
Add following code to Global.asax.cs file.
Global.asax.cs
protected void Application_Start(object sender, EventArgs e) { AddNewFieldToPagesModule(); } private void AddNewFieldToPagesModule() { var metaManager = Telerik.Sitefinity.Data.Metadata.MetadataManager.GetManager(); var type = metaManager.GetMetaType(typeof(Telerik.Sitefinity.Pages.Model.PageData)); if (type == null) { type = metaManager.CreateMetaType(typeof(PageData)); metaManager.SaveChanges(); } App.WorkWith() .DynamicData() .Type(typeof(PageData)) .Field() .TryCreateNew("IsErrorPage", typeof(string)) .SaveChanges(true); App.WorkWith() .Pages() .LocatedIn(PageLocation.Frontend) .ThatArePublished() .ForEach(p => { if (p.Page.DoesFieldExist("IsErrorPage")) { var val = p.Page.GetValue("IsErrorPage"); if (val == null || (val != null && string.IsNullOrEmpty(val.ToString()))) p.Page.SetValue("IsErrorPage", "false"); } }) .SaveChanges(); }
This will add a new field named IsErrorPage to your Pages module and set value false by default. This is what will be used in Application_Error event (highlighted code).
Step 3
Add following 3 files under folder Sitefinity > Services > Pages. This service will be consumed in step 4.
IPagesExtendedService.cs
using System.ServiceModel; using System.ServiceModel.Web; namespace Telerik.Sitefinity.Modules.Pages.Web.Services { [ServiceContract(Namespace = "Telerik.Sitefinity.Modules.Pages.Web.Services")] public interface IPagesExtendedService { [OperationContract] [WebGet(UriTemplate = "/SetErrorPage/{lnk}", ResponseFormat = WebMessageFormat.Json)] bool SetErrorPage(string lnk); } }
PagesExtendedService.cs
using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.Web; using Telerik.Sitefinity.Fluent.Pages; using Telerik.Sitefinity.Model; namespace Telerik.Sitefinity.Modules.Pages.Web.Services { [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class PagesExtendedService : IPagesExtendedService { public bool SetErrorPage(string lnk) { if (string.IsNullOrEmpty(lnk)) return false; lnk = HttpUtility.UrlDecode(lnk.ToLower()); lnk = lnk.Replace("|", "/").TrimStart('/'); var node = App.WorkWith() .Pages() .LocatedIn(PageLocation.Frontend) .ThatArePublished() .Get() .Where(i => lnk.Contains(i.UrlName.ToString().ToLower())) .FirstOrDefault(); if (node != null && node.Page != null) { App.WorkWith() .Pages() .LocatedIn(PageLocation.Frontend) .ThatArePublished() .ForEach(i => { i.Page.SetValue("IsErrorPage", "false"); }) .SaveChanges(); node.Page.SetValue("IsErrorPage", "true"); return true; } return false; } } }
PagesExtendedService.svc
<%@ ServiceHost
Language="C#"
Debug="false"
Service="Telerik.Sitefinity.Modules.Pages.Web.Services.PagesExtendedService"
Factory="Telerik.Sitefinity.Web.Services.WcfHostFactory"
%>
Step 4
Go to Administration > Backend Pages. Edit Pages node and add following JavaScript using Scripts widget.
var CHECK_IF_GRID_LOADED = setInterval(function () { var actions = $('div.sfMoreActions'); if (actions.length > 0) { clearInterval(CHECK_IF_GRID_LOADED); actions.each(function () { var action = $(this); $('ul.actionsMenu', action).each(function () { var lnk = $('> a', action.next()).attr('href'); var anc = $('<a>Set as Error page</a>').attr('href', 'javascript:void(0);').attr('data-pagelink', lnk).bind('click', function () { setErrorPage(this); }); var li = $('<li></li>').append(anc); $('ul.innerBox .sfSeparator', this).eq(0).append(li).append('<li class="sfSeparator"><strong class="sfSepNoTitle"></strong></li>'); }); }); } }, 5000); function setErrorPage(sender) { sender = $(sender); if (sender) { var lnk = sender.attr('data-pagelink'); if (lnk == undefined) return; lnk = lnk.replace('https://', '').replace('http://', ''); var arr = lnk.split('/'); delete arr[0]; lnk = arr.join('|'); var postLnk = '/Sitefinity/Services/Pages/PagesExtendedService.svc/SetErrorPage/' + lnk; $.ajax( { url: postLnk, success: function (result) { } }); } }
This will add a new action link in the drop-down menu, so that you can easily set Error page any time. This script will consume service we created in step 3. Service will set IsErrorPage field value to false for all pages except the one set as Error page.
So, now you can select any page as Error page and will never get into redirect loop situation.
Happy Coding!
And we know you want to learn more! So, register now for FalafelCon 2014 and perfect your skills, expand your horizons, and get inspired.
The post Set Error page through UI and avoid redirect loop appeared first on Falafel Software Blog.