ELMAH and custom error page

1,800 views
Skip to first unread message

soccerdad

unread,
Sep 24, 2007, 5:30:33 PM9/24/07
to ELMAH
I'm planning on integrating ELMAH into a critical application this
week. In the past, when I've used similar techniques to do error
loggng and emailing, I've used web.config settings to optionally call
"Server.ClearError" and then redirect to a custom application error
page. Best I can tell, ELMAH doesn't support that capability (which is
fine).

My question is what's the "best practice" means to accomplish what I
described. Should I handle Application_Error in global.asax and do
those things? Are other folks doing something different?

Thanks,

Donnie

Scott Wilson

unread,
Sep 24, 2007, 5:34:21 PM9/24/07
to el...@googlegroups.com

--- soccerdad <donni...@gmail.com> wrote:
> My question is what's the "best practice" means to accomplish what I
> described. Should I handle Application_Error in global.asax and do
> those things? Are other folks doing something different?

The best practice would be to use Elmah as-is and to configure a custom
error page in the web.config file.

Elmah will catch errors and log them without any interference on the rest
of the application. If you want a custom error page, that can easily be
set up in the standard ASP.NET web.config CustomErrors section. Neither
will affect the operation of the other.

Regards,

Scott

Atif Aziz

unread,
Sep 24, 2007, 6:08:27 PM9/24/07
to el...@googlegroups.com
If you want to be a good citizen of the web, my recommendation is to never issue a redirection in case of an error but rather to use Server.Execute to emit a custom error page in the same HTTP transaction and let the response fail. The problem with a redirection is that you're giving a false positive to the client and trump it. That is, there is indeed an error in obtaining the actual resource identified by the URL yet you are re-directing (302 Found) to another resource. That other resource is a page indicating error the user but the agent itself ends up (paradoxically) treating the whole transaction as 200 OK (in HTTP parlance). So you should let the 404, 500 or whatever fault code bleed through back to the user agent and at the same time use a Server.Execute to emit a custom error page as the HTTP response body (no re-direction). It is a common misconception that you cannot return some HTML response even if the HTTP transaction ends in error and yet this is happening all the time (even with standard ASP.NET error pages). Given this, you should now be thinking that the default custom error handling in ASP.NET is in fact broken, and sadly, it is. :)

> Should I handle Application_Error in global.asax and do
> those things?

Yes, do a Server.Execute to your custom error page within there. You can use the GetHttpCode method of the HttpException object to find out which custom error page to display (for example, 404.aspx, 500.aspx, etc.). One problem I've noticed with ASP.NET is that Server.Execute resets the status code of the HTTP response so you have to tuck that away in a local variable on the stack and set it back on Response.StatusCode once your Server.Execute returns. Finally, you can issue a Server.ClearError before Application_Error returns. Fortunately, none of this affects ELMAH's ability to log the exception at the same time.

- Atif

soccerdad

unread,
Sep 25, 2007, 3:32:32 PM9/25/07
to ELMAH
So if I were to pseudocode the Application_Error handler (let's assume
I just have one page for all error types), it might look like:

void Application_Error(object sender, EventArgs e)
{
Exception uncaught = Server.GetLastError();
if (uncaught is HttpUnhandledException && uncaught.InnerException !
= null)
{
uncaught = uncaught.InnerException;
}

int origCode = ((HttpException) uncaught).GetHttpCode();

Server.Execute("~/MyCustomErrorPage.aspx");

HttpContext.Current.Response.StatusCode = origCode;

Server.ClearError():
}

Is that about right? One thing I really want to make sure of is that
we stop getting unhandled error entries in the system event log.

I appreciate the input.

Donnie

Atif Aziz

unread,
Sep 25, 2007, 3:40:46 PM9/25/07
to el...@googlegroups.com
> Is that about right?

Yep, some simplifications aside, that's about right.

soccerdad

unread,
Sep 25, 2007, 5:16:35 PM9/25/07
to ELMAH
Thanks again.

Unfortunately, Server.Execute is throwing a spurious error about
"Cannot use a leading .. to exit above the top directory" when I
execute the error page. If I run this same page normally (via
browser), it works fine. Based on the URL being resolved in the
callstack, I've proven that the page refers to the URL as "~/images/
whatever.jpg" but by the time it gets to the .net method in question,
it looks like "/../images/whatever.jpg". If I get rid of the leading
'~' (since my app is at the root, it would work), sure enough the
error goes away but then shows up for any other URL in the app of the
form "~/someotherfile.ext" (obviously there are zillions of those).

Assuredly as asp.net bug with the weird situation of Server.Execute in
the middle of handling an error for a page that had been executing in
a standard context.

Oh well - will have to revisit it later.

Donnie

soccerdad

unread,
Oct 2, 2007, 2:36:19 PM10/2/07
to ELMAH
In case people stumble across this thread and run into the problem I
did with Server.Execute, here's what I was able to get working in the
Global.asax.cs Application_Error handler:

object o = BuildManager.CreateInstanceFromVirtualPath(
"~/ErrorPage.aspx", typeof(IHttpHandler));
StringWriter sw = new StringWriter();
Server.Execute((IHttpHandler) o, sw, false);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = // Code from
Server.LastError();
HttpContext.Current.Response.Write(sw.ToString());
Server.ClearError();

That code seems to be working well, and accomplishes the two goals I
had: a) Preventing entries in the system event log; b) adhering to
Atif's recommendation that the error page return a semantically
accurate HTTP status code. Note that the customErrors mode in
Web.config must be "Off" for Application_Error to get called in the
first place.

FWIW...

Donnie

Atif Aziz

unread,
Oct 2, 2007, 3:18:45 PM10/2/07
to el...@googlegroups.com
Thanks for sharing your findings, Donnie. Incidentally, I checked-in some code into the DotNetKicks project that returns custom error pages as I had suggested earlier in our thread. You might want to check out Application_Error in Global.asax.cs over at:
http://dotnetkicks.googlecode.com/svn/trunk/DotNetKicks/Incremental.Kick.Web.UI/Global.asax.cs

It's a working example of what I was talking about that's deployed in production. I'm not sure I see why you had to go through the BuildManager rather than being able to rely on just Server.Execute, but perhaps having the above code may help shed some light.

- Atif

-----Original Message-----
From: el...@googlegroups.com [mailto:el...@googlegroups.com] On Behalf Of soccerdad
Sent: Tuesday, October 02, 2007 8:36 PM
To: ELMAH
Subject: [ELMAH] Re: ELMAH and custom error page

Atif Aziz

unread,
Oct 2, 2007, 3:24:56 PM10/2/07
to el...@googlegroups.com
> Note that the customErrors mode in
> Web.config must be "Off" for Application_Error to get called in the
> first place.

Interesting. It's not been my observation at all. Are we using the same ASP.NET here considering all the discrepancies? :) AFAIK, Application_Error has always been called unconditionally. In all my apps, I've left the customError mode to remoteOnly.

- Atif


-----Original Message-----
From: el...@googlegroups.com [mailto:el...@googlegroups.com] On Behalf Of soccerdad
Sent: Tuesday, October 02, 2007 8:36 PM
To: ELMAH
Subject: [ELMAH] Re: ELMAH and custom error page

Atif Aziz

unread,
Oct 2, 2007, 3:31:47 PM10/2/07
to el...@googlegroups.com
BTW, just saw this blog post:
http://professionalaspnet.com/archive/2007/09/30/Properly-Using-Custom-Error-Pages-in-ASP.NET.aspx

The rationale under the section, "Custom Error Handling in ASP.NET and Status Codes," seems plain wrong to me. Rich seems to be hitting the right note here in response:
http://professionalaspnet.com/archive/2007/09/30/Properly-Using-Custom-Error-Pages-in-ASP.NET.aspx#395

The following blog post is a nice eye-opener into just how much of what we assume and do is wrong or misunderstood:
http://coderjournal.com/2007/04/world-of-http11-status-codes/

- Atif


-----Original Message-----
From: el...@googlegroups.com [mailto:el...@googlegroups.com] On Behalf Of soccerdad
Sent: Tuesday, October 02, 2007 8:36 PM
To: ELMAH
Subject: [ELMAH] Re: ELMAH and custom error page

Eric

unread,
Oct 2, 2007, 4:46:50 PM10/2/07
to ELMAH
BTW, another article http://msdn2.microsoft.com/en-us/library/aa479319.aspx
about this topic in general.

- Eric

soccerdad

unread,
Oct 3, 2007, 9:44:26 AM10/3/07
to ELMAH
This is a thread at forums.asp.net where I give more detail on the
Server.Execute issue:

http://forums.asp.net/t/1165013.aspx

I was in contact with a number of people, including Scott Guthrie and
another developer at MS, about Server.Execute. They basically
confirmed that calling Server.Execute in the context of a different
relative path than the currently executing page could / would cause
problems. Rick Strahl also confirmed this expectation. The MS
developer said that the BuildManager approach was a sound one and that
it shouldn't have any issues in a medium-trust environment.

FWIW...

Donnie

On Oct 2, 3:18 pm, Atif Aziz <Atif.A...@skybow.com> wrote:
> Thanks for sharing your findings, Donnie. Incidentally, I checked-in some code into the DotNetKicks project that returns custom error pages as I had suggested earlier in our thread. You might want to check out Application_Error in Global.asax.cs over at:http://dotnetkicks.googlecode.com/svn/trunk/DotNetKicks/Incremental.K...

soccerdad

unread,
Oct 3, 2007, 9:54:00 AM10/3/07
to ELMAH
The code you pointed to is very close to what I was originally trying
to use (and, in fact, would be using if Server.Execute wasn't
barfing). And I can't say why my Application_Error handler doesn't get
called if customErrors mode="On". It will get called if it's "Off" or,
in a dev environment, if it's "RemoteOnly". I might go back and re-
check that, but I consistently had that issue late last week.

I do agree with you - a 302 / 200 is not an appropriate response to a
real error. I may check MS' issues site and open a case suggesting
that customErrors be enhanced to behave the way we've developed based
on new attributes in the Web.config, e.g. 'preserveStatusCode="true"'.

Thanks for all the feedback and links on this topic.

Donnie

On Oct 2, 3:18 pm, Atif Aziz <Atif.A...@skybow.com> wrote:
> Thanks for sharing your findings, Donnie. Incidentally, I checked-in some code into the DotNetKicks project that returns custom error pages as I had suggested earlier in our thread. You might want to check out Application_Error in Global.asax.cs over at:http://dotnetkicks.googlecode.com/svn/trunk/DotNetKicks/Incremental.K...

Atif Aziz

unread,
Oct 3, 2007, 11:20:50 AM10/3/07
to el...@googlegroups.com
Hi Donnie,

OK, I can imagine this could turn out to be an issue because I've simply come across such oddities in ASP.NET before, but I can't seem to reproduce it for the life of me. Want to take a crack at the attached plain-vanilla solution? It has the setup (I think) you described in your ASP.NET Forums thread and Server.Execute seems to be working fine along with image references.

ErrorWeb.zip

Atif Aziz

unread,
Oct 3, 2007, 11:24:03 AM10/3/07
to el...@googlegroups.com
> I may check MS' issues site and open a case suggesting
> that customErrors be enhanced to behave the way we've
> developed based on new attributes in the Web.config,
> e.g. 'preserveStatusCode="true"'.

If you mean submitting it as a bug (and a bug it is) over at http://connect.microsoft.com/VisualStudio then you'll get my vote. :)

Atif Aziz

unread,
Oct 3, 2007, 11:58:40 AM10/3/07
to el...@googlegroups.com
Eric, this article ("Rich Custom Error Handling with ASP.NET") does a very thorough exploration of the what and how in ASP.NET with respect to exception handling on the server. It's unfortunate, though, that it does not at all bring to light the incorrect implementation of custom error pages and how to work-around that today. In fact, the section, "Passing Control," is quite misleading.

>>
The final task of Application_Error is to execute the Redirect() or Transfer(). From the discussion of configuring behavior above, you already know that which of these you choose is tied to how the Exception is stored, and that some combinations work while others don't.
<<

The decision to use Redirect or Transfer is very fundamental to what you're communicating to the user agent from the point of view of the HTTP protocol and nothing to do with how the Exception is stored.

>>
The features of the storage methods usually drive the decision. But occasionally, features of Redirect() and Transfer() drive the decision.
<<

Again, the features don't drive the decision here, but the protocol. And this part gets interesting where the rationale comes in...

>>
If this seems to be an argument in favor of Transfer, it isn't. The built-in customErrors feature uses Redirect and not Transfer for a reason. The rationale of the ASP.NET development team is that Redirect accurately displays the URL of the custom error page, while Server.Transfer is intended for "switchboard"-style pages (as on content management sites) where the true URL is preferably hidden.
<<

Redirect accurately displays the URL of the custom error page? Who cares about the URL or the error page? That's precisely what you want to hide! It's an implementation detail of your server-side.

- Atif


-----Original Message-----
From: el...@googlegroups.com [mailto:el...@googlegroups.com] On Behalf Of Eric
Sent: Tuesday, October 02, 2007 10:47 PM
To: ELMAH
Subject: [ELMAH] Re: ELMAH and custom error page

Eric

unread,
Oct 3, 2007, 3:15:14 PM10/3/07
to ELMAH
Atif,

Thanks for your answer.

If I understand it correctly, only Redirect will send something to the
agent (Redirect Code). Transfer/Execute will not violate the
protocol.

I agree that in the article has the wrong 'feature' view.

I think the reason for his article was the problem with the built-in
customErrors feature,
where you loose all the exception information in the error page.

So he build a vehicle around the different transfer/redirect methods
and for transfering the error infos via context/session/querystring
etc
to show a full featured error page.

Exaggerated, but I was blended too.

Eric


Reply all
Reply to author
Forward
0 new messages