I previously posted a possible solution for handling 404 exceptions in EPiServer websites, but here's an alternative and possibly more elegant way of configuring custom error redirects for EPiServer.
What I wanted to accomplish
I wanted to be able to specify a PageID for 404 File Not Found errors and another one for 500 Server Exception errors and have these EPiServer pages act as error messages. So, I started by adding the following keys to my appSettings section in web.config:
<add key="FileNotFoundPageId" value="481"/>
<add key="GenericErrorPageId" value="482"/>
A birds-eye view of the solution
Here's the algorithm in short:
- Check if customErrors is enabled in web.config
- If so, check if there are redirects specified for 404 and 500 errors
- If not, add new redirects
- If redirects exist, validate their respective redirect URL
- Add/modify custom error redirects as necessary
- Save web.config
The code
Here's the method that I now call from the
Application_Start() method in global.asax:
/// <summary>
/// Performs customErrors configuration based on
/// related app settings
/// </summary>
public static void ConfigureCustomErrors()
{
Configuration config =
WebConfigurationManager.OpenWebConfiguration("~");
CustomErrorsSection section =
(CustomErrorsSection)config.GetSection(
"system.web/customErrors");
//Verify that customErrors exists in web.config
if (section != null)
{
//Only configure if customErrors is enabled
if (section.Mode != CustomErrorsMode.Off)
{
if(!section.IsReadOnly() &&
!section.SectionInformation.IsLocked)
{
//Collection of new redirects to add to
//the customErrors element
CustomErrorCollection redirectsToAdd =
new CustomErrorCollection();
//Page ID of the page to be used for
//custom error redirects
int redirectPageId = 0;
//Get existing redirects, if any
CustomError redirect404 =
section.Errors["404"];
CustomError redirect500 =
section.Errors["500"];
//Get URL for 404 redirects
int.TryParse(
ConfigurationManager.AppSettings[
"FileNotFoundPageId"],
out redirectPageId);
string fileNotFoundURL =
ToolBox.GetSimpleAddress(
DataFactory.Instance.GetPage(
new PageReference(redirectPageId));
//Get URL for server error redirects
int.TryParse(
ConfigurationManager.AppSettings[
"GenericErrorPageId"],
out redirectPageId);
string serverErrorURL =
ToolBox.GetSimpleAddress(
DataFactory.Instance.GetPage(
new PageReference(redirectPageId)));
//If the 404 redirect hasn't been
//specified or if its redirect
//URL is invalid
if (fileNotFoundURL!=string.Empty &&
(redirect404 == null ||
redirect404.Redirect!=
fileNotFoundURL))
{
//Add new
if (redirect404 == null)
{
CustomError fileNotFoundError =
new CustomError(404,
fileNotFoundURL);
redirectsToAdd.Add(
fileNotFoundError);
}
else //Modify existing
{
redirect404.Redirect =
fileNotFoundURL;
}
}
//If the 500 redirect hasn't been
//specified or if its redirect
//URL is invalid
if (fileNotFoundURL != string.Empty &&
(redirect500 == null ||
redirect500.Redirect !=
fileNotFoundURL))
{
//Add new
if (redirect500 == null)
{
CustomError serverError =
new CustomError(500,
serverErrorURL);
redirectsToAdd.Add(serverError);
}
else //Modify existing redirect
{
redirect500.Redirect =
serverErrorURL;
}
}
//Add any new redirects
foreach (
CustomError redirectToAdd in
redirectsToAdd)
{
section.Errors.Add(redirectToAdd);
}
//Save web.config if its
//contents have changed
config.Save();
}
}
}
}
The result
When the EPiServer website application starts, the web.config file includes the following element within the system.web section:
<customErrors mode="On" />
After the ConfigureCustomErrors() method above has executed, web.config looks like this:
<customErrors mode="On">
<error statusCode="404" redirect="/404" />
<error statusCode="500" redirect="/error" />
</customErrors>
Now, if I manually modify the URLs of these redirects and save web.config the website will restart and the redirects will be re-configured with the correct friendly URLs:
Manual edit:
<customErrors mode="On">
<error statusCode="404" redirect="/my404.aspx" />
<error statusCode="500" redirect="/error.aspx" />
</customErrors>
After the website application has restarted:
<customErrors mode="On">
<error statusCode="404" redirect="/404" />
<error statusCode="500" redirect="/error" />
</customErrors>
And to sum up
Pretty slick, huh?
Update
Don't forget to configure file permissions for web.config so that the ASPNET account is able to modify it.
Update #2
Unless you use wildcard mapping for all MIME types in IIS 6, you may have to set the 404 Custom Error property in IIS to an .aspx URL which does not exist. This will cause requests that are not handled by ASP.NET to trigger another request that will - and result in a 404. This 404 will in turn be handled by the custom error handling we've painstakingly implemented above! :)