Last active
August 29, 2015 14:14
-
-
Save timw255/f0c10de9be1dcb98b0d6 to your computer and use it in GitHub Desktop.
Custom Errors Module - Proof of Concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protected void Application_Error(object sender, EventArgs e) | |
{ | |
// check to see if something horrible is going on... | |
if (Request.Headers["Custom-Error-Request"] != null) | |
{ | |
// looks like a request to grab an error page ended up here. let's bail. | |
return; | |
} | |
// get the last error | |
HttpException err = Server.GetLastError() as HttpException; | |
Server.ClearError(); | |
// try to skip using any other custom error settings | |
Response.TrySkipIisCustomErrors = true; | |
// check to see if we need to send back something pretty | |
var acceptsHtml = Request.Headers["Accept"].Contains("text/html"); | |
// we're on! | |
if (err != null && acceptsHtml) | |
{ | |
// get the language from the visitor's browser settings | |
string lang = (Request.UserLanguages ?? Enumerable.Empty<string>()).FirstOrDefault(); | |
var culture = CultureInfo.GetCultureInfo(lang); | |
// set the culture to match the browser language | |
Thread.CurrentThread.CurrentUICulture = culture; | |
PageManager pageManager = PageManager.GetManager(); | |
PageNode pNode = null; | |
// this is where we would put a manager to do some fancy lookups based on the error codes | |
switch (err.GetHttpCode()) | |
{ | |
case 404: | |
pNode = pageManager.GetPageNodes().Where(pN => pN.Title == "404").FirstOrDefault(); | |
break; | |
case 500: | |
default: | |
pNode = pageManager.GetPageNodes().Where(pN => pN.Title == "Generic").FirstOrDefault(); | |
break; | |
} | |
// grab the URL of the error page we're going to display to the visitor | |
string url = pNode.GetFullUrl(culture, true); | |
url = UrlPath.ResolveUrl(url, true, true); | |
// grab the entire HTML of the page | |
WebClient client = new WebClient { Encoding = Encoding.UTF8 }; | |
client.Headers.Add("Custom-Error-Request", ""); | |
string html = err.GetHttpCode().ToString(); | |
try | |
{ | |
html = client.DownloadString(url); | |
} | |
catch (WebException ex) | |
{ | |
// the error page is probably blowing up. maybe do some logging...or send an email? | |
} | |
// send back the the best response we got for the error and a status code | |
Response.Write(html); | |
Response.StatusCode = err.GetHttpCode(); | |
} | |
else if (err != null) | |
{ | |
// just fire back a basic message and the status | |
Response.Write(err.GetHttpCode().ToString()); | |
Response.StatusCode = err.GetHttpCode(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<customErrors mode="On"> | |
<error statusCode="404" redirect="~/8ec96125-02ce-47e2-a545-0eba4192dc7d" /> | |
</customErrors> | |
<httpErrors errorMode="Custom"> | |
<remove statusCode="404" subStatusCode="-1" /> | |
<error statusCode="404" prefixLanguageFilePath="" path="/CustomErrorPages/8ec96125-02ce-47e2-a545-0eba4192dc7d.aspx" responseMode="ExecuteURL" /> | |
</httpErrors> |
I hadn't thought about the accept header thing, good call.
The language sniffing was done because I want to grab the page and return it under the same URL, along with the error code. Because I'm not doing a redirect (spinning up the WebRequest manually) I didn't think I could rely on Sitefinity to return the correct page. I admit that I might be over-complicating it though. 😄
If WebClient is faster and returns the same result, I'm totally making the switch.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice one, Tim. Some comments:
It might be a good idea to check for the Accept header and abort if the value is anything other than
text/html
or similar. No need to display a custom error page for broken style sheet or image links where no one will ever see the actual error page unless they open their browser console.Lines 15 to 19: I'm not sure if it is necessary to manually grab the client's language preference. This should have been done by Sitefinity's HTTP module earlier, shouldn't it? If not, you need to account for missing language preferences as well as language prefs that are not covered by the application, i.e. a user has their language pref set to French but the site can only offer English and Spanish -- this requires a fallback. I've built something similar along these lines once (hastily adapted in a text editor and not tested, may contain errors):
Lines 42 to 49: Grabbing the HTML is a lot easier with WebClient, like so:
What happens if the custom error page can't be reached and it itself returns 404? Failures need to be accounted for.