I’ve been including Phil Winstanley’s error reporting code (http://weblogs.asp.net/plip/archive/2004/04/17/114984.aspx) in our projects for quite some time now and it’s become an invaluable resource when debugging site exceptions both while developing and on live sites.
Since the initial release Phil’s class, we’ve been expanding it in various ways to suit so I thought in addition to the trace output work around that I posted about a while ago I thought I would share some other techniques we use.
Quickly turning it off while developing
I have seen people writing checks to ensure the site isn’t local before reporting the error but rather than hard coding that we’ve added a value to the web.config “SendErrors” it it’s true then the error report is sent.
//A flag in the web.config file to allow you to disable error reporting while testing (if you want to!)
bool sendErrors = Convert.ToBoolean(
ConfigurationManager.AppSettings[
"SendSiteErrors"]);

if (sendErrors)
{...}
Check what sort of error it is
There’s no point in throwing an exception error if the error message is a 404 so check the error type and redirect the user to a specific error page for the given error page, i.e. for a 404 you could display the URL they attempted to visit explain what may have happened. How about doing a little selling to them while they’re there? Or as some companies have started doing, offer them a discount for the inconvienience!
Make it more generalised
In Phil’s class he allows you to set the site name for the error report, we generally just wrote the domain name in for simplicity, so instead, just use: Request.Url.Host

WE.Site = context.Url.Host;
Rid yourself of Spambot attacks
Sean Ronan from Activepixels was showing me his method of getting around the spambot attacks that are becoming a daily annoyance once the method was implemented it cut our dud error reports from up to 400 per site per night to around 0 :)
Sean noticed that although the spambots were getting clever and identifying ASP.Net forms to post back to, they were also altering the __VIEWSTATE field which caused a System.FormatException error to be thrown.
If you’re also using Phil Winstanley's ASP.Net WebException handler you’ll no doubt have been inundated with error reports, the main issue imho is the flood count, if you were getting 50 form posts, you’re likely to miss the important one.
The solution is remarkably simple: if the error message is a System.FormatException then simply check if the one of the form fields is “__VIEWSTATE” and has “Content-Type” as part of it’s content, if it does then don’t report it –easy!
//Check the error type
if (CheckForErrorType(currentError,
"System.FormatException"))
{...}

{
//Ensure there's a form collection to parse
if (context.Form.Count >
0)
{...}

{
//Loop through each item in the form collection to check for _VIEWSTATE
foreach (
string key
in context.Form)
{...}

{
//If there's a viewstate match and within it "Content-Type" (used by the spambots) then return
if (key.IndexOf(
"_VIEWSTATE") >
0 && context.Form[key].ToString().IndexOf(
"Content-Type") >
0)
return;

}

}

}
An example of the “spambotted” ViewState courtesy of Infobrokers:
__VIEWSTATE
[email protected] Content-Type: multipart/mixed; boundary="===============0240301364==" MIME-Version: 1.0 Subject: 342f9a6 To:
[email protected] bcc:
[email protected] From:
[email protected] This is a multi-part message in MIME format. --===============0240301364== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit oos --===============0240301364==--
Persisting the code across projects
There are perhaps better ways of persisting the code across projecst but we’ve copied the various code snippets for the Global.asax and Web.Config file onto the Visual Studio Toolbox offering us drag and drop setup.
One better alternative would be to add it to the Global.asax template, you can find it at:
%ProgramDir%\Microsoft Visual Studio 8\Common7\IDE\ItemTemplatesCache\Web\CSharp\1033\GlobalAsax.zip
Just paste your code in there and bobs your uncle. I’ve got a feeling you can write your own project templates too if you generate that many projects.
Eventually I expect I’ll wrap Phil’s WebException with our own generic handler that does all this for us but for now I’m happy with this method.
Enjoy.
The relevant code in its entirety:
//Declare for the scope of the class
private static HttpRequest context =
HttpContext.Current.Request;

void Application_Error(
object sender,
EventArgs e)
{...}

{
//A flag in the web.config file to allow you to disable error reporting while testing (if you want to!)
bool sendErrors = Convert.ToBoolean(
ConfigurationManager.AppSettings[
"SendSiteErrors"]);

if (sendErrors)
{...}

{
Deal with 404's
#region Deal with
404's
//Redirect the user to a friendly page
if (CheckForErrorType(currentError,
"FileNotFound"))

RedirectToFriendlyUrl(
"Your404ErrorPage.htm");

#endregion
Deal with Spambots
#region Deal with Spambots

//Check the error type
if (CheckForErrorType(currentError,
"System.FormatException"))
{...}

{
//Ensure there's a form collection to parse
if (context.Form.Count >
0)
{...}

{
//Loop through each item in the form collection to check for _VIEWSTATE
foreach (
string key
in context.Form)
{...}

{
//If there's a viewstate match and within it "Content-Type" (used by the spambots) then return
if (key.IndexOf(
"_VIEWSTATE") >
0 && context.Form[key].ToString().IndexOf(
"Content-Type") >
0)
return;

}

}

}

#endregion
//Enable the trace for the duration of the error handling
//Note: See previous blog post about this not working in the latest
//version of the reporting code (at time of writing v4)
TraceContext t =
HttpContext.Current.Trace;
bool bCurrentState = t.IsEnabled;

t.IsEnabled =
true;
Handle the Exception
#region Handle the
Exception

ErrorHandling.
WebException WE =
new ErrorHandling.
WebException();

WE.CurrentException = currentError;

WE.MailFrom =
"[email protected]";

WE.MailTo =
"[email protected]";

WE.MailAdmin =
"[email protected]";

WE.Site = context.Url.Host;

WE.SmtpServer =
"localhost";

WE.FloodCount =
10;

WE.FloodMins =
5;

#endregion
Choose what you're interested in
#region Choose what you
're interested in

WE.ReturnCache =
true;

WE.DrillDownInCache =
true;

WE.IncludeApplication =
true;

WE.IncludeBrowser =
true;

WE.IncludeEnvironmentVariables =
true;

WE.IncludeForm =
true;

WE.IncludeProcess =
true;

WE.IncludeQueryString =
true;

WE.IncludeRequestCookies =
true;

WE.IncludeRequestHeader =
true;

WE.IncludeResponseCookies =
true;

WE.IncludeServerVariables =
true;

WE.IncludeSession =
true;

WE.IncludeTrace =
true;

WE.IncludeVersions =
true;

WE.IncludeAuthentication =
true;

#endregion
//Send off the error report
WE.Handle();

//Return the trace to its original state
t.IsEnabled = bCurrentState;

//Redirect the user to a friendly page
RedirectToFriendlyUrl(
"Your500ErrorPageHere.htm");

}

}

protected void Application_PreRequestHandlerExecute(
Object sender,
EventArgs e)
{...}

{
//WebResource.axd doesn't implement IRequireSessionState so will throw an error without this check
if (Context.Handler
is IRequiresSessionState || Context.Handler
is IReadOnlySessionState)

ErrorReporting.
SessionTracker.AddRequest(
"Pre Request Handler Execute",
true,
true,
false);

}

/// <summary>
/// Helper method to check what sort of error we're dealing with
/// </summary>
/// <param name="ex">Exception to check</param>
/// <param name="errorText">String value to find</param>
/// <returns>true if found, false if not</returns>
private static bool CheckForErrorType(
Exception ex,
string errorText)
{...}

{
if (ex !=
null)
{...}

{
//Check the exception if it's not found in the outer ex, check it's children
if (ex.GetType().ToString().IndexOf(errorText) >
0)
return true;
else
return CheckForErrorType(ex.InnerException, errorText);

}
else
{...}

}

/// <summary>
/// Redirects the user to a given url in the event of an error
/// </summary>
/// <param name="Url">URL of the page to redirect to</param>
private static void RedirectToFriendlyUrl(
string Url)
{...}

{
//Only redirect the user if the URL is not empty and we're not on a dev machine
//TODO: Check the referrer to ensure we don't redirect the user to the page causing the error!
if (!
String.IsNullOrEmpty(Url) && (context.Url.Host.IndexOf(
"localhost") <
0))
HttpContext.Current.Response.Redirect(Url);

}
Finally: Since playing with Atlas/AJAX I've found that the errors are caught by the Atlas engine, I've worked out a way around it and will post about it later.
Liked this post? Got a suggestion? Leave a comment