I've currently got httpErrors setup to deal with 500's here:-
<httpErrors errorMode="Custom" existingResponse="Replace">
......
<remove statusCode="500"/>
<error statusCode="500" path="/server-error" responseMode="ExecuteURL"/>
</httpErrors>
This works fine, but in the case where iis receives the error I still get the yellow screen of death. An example is when entity framework can not connect to the database and I receive:-
Cannot open database "Test-DB" requested by the login. The login failed. Login failed for user 'sa'.
I've setup customErrors to deal with this:-
<customErrors mode="On" defaultRedirect="error.html" redirectMode="ResponseRedirect">
<error statusCode="500" redirect="error.html" />
</customErrors>
which works as expected as long as there is no modules without preCondition="managedHandler".
I have a few modules which deal with images and css files and are in the same project.
<modules runAllManagedModulesForAllRequests="false">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
<add name="ImageHandler" type="foo.bar.ProductImageHandlerHttpModule" />
<add name="CustomCssHandler" type="foo.bar.CustomCssHttpModule" />
<add name="Glimpse" type="Glimpse.AspNet.HttpModule, Glimpse.AspNet" preCondition="integratedMode" />
</modules>
Comment these out and I get the error.html, keep them in and I get
Runtime Error Description: An exception occurred while processing your
request. Additionally, another exception occurred while executing the
custom error page for the first exception. The request has been
terminated.
showing that a module from the project is also erroring when trying to show the error.html.
Does anyone know a fix/workaround?
Its a tough situation - your error comes from HttpModule, which is run for every single request - including request to error.html page. One way is to route static files (such as error.html) just via your server - and ignore them on .NET level; this may not be always possible (sometimes handling static files on .NET level is handy). The only other way I can think off is hook in global.asax to error event and handle it yourself (which will ignore the customErrors)
Something like:
public class Global : System.Web.HttpApplication
{
protected void Application_Error(object sender, EventArgs e)
{
// is there an error ?
var error = Server.GetLastError();
if (error != null)
{
// mark the error as - "i'll handle it myself"
Server.ClearError();
// load error.html manually & dump it to response
var content = File.ReadAllText(Server.MapPath("~/error.html"));
Context.Response.Write(content);
// set correct error code
Context.Response.StatusCode = 500;
}
}
}
Note: this can be ironed out, but you see the general principle ...
As #Ondrej said, another error occurred when handling the custom error page comes from unhandled exception on HttpModule, thus it is necessary to either skip or bypassing customErrors section on web.config file.
This code also includes how to skip custom errors generated from IIS, besides generating custom HTML error page:
protected void Application_Error(Object sender, EventArgs e)
{
// Get last error occurred
var exception = Server.GetLastError();
// catch unhandled exceptions
if (exception is HttpUnhandledException)
{
// handle the error by ASP .NET itself
Server.ClearError();
// write status code handling
HttpContext.Current.Response.WriteFile(Server.MapPath("~/error.html"));
HttpContext.Current.Response.StatusCode = 500;
HttpContext.Current.Response.StatusDescription = "Internal Server Error";
// set ASP .NET handlers instead of using IIS handlers
HttpContext.Current.Response.TrySkipIisCustomErrors = true;
}
}
Additionally, to get rid of YSOD pages generated by IIS you may set existingResponse attribute to pass error handling into ASP .NET Application_Error method as this:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="PassThrough">
<remove statusCode="500" />
<error statusCode="500" responseMode="File" path="/error.html" />
</httpErrors>
</system.webServer>
The reason behind those settings based from Kanwaljeet Singla's explanation (source here):
existingResponse
Value of this section level property tells custom error module what to
do when response text is not blank. If a module call
IHttpResponse::SetStatus to set an error code and also sets up
response text, existingResponse property tells if custom error module
should replace current response text with its own error text or should
it let the current response text pass through. Asp.Net and WCF are
example of modules which sets error response text. This property can
be set to following three values.
Replace – This value make custom error module to always replace the error information with text generated by custom error module. If
existingResponse is set to “Replace”, errors/exceptions generated by
Asp.Net/WCF are replaced by IIS7 errors.
PassThrough – If existingResponse is seen as “PassThrough”, custom error module will always pass through responses from modules.
This setting will make custom error module return blank response if
modules don’t set any text.
Auto – This is the default value and tells custom error module to do the right thing. Actual error text seen by clients will be
affected depending on value of fTrySkipCustomErrors returned in
IHttpResponse::GetStatus call. When TrySkipCustomErrors is set to
true, custom error module will let the response pass through but if it
is set to false, custom errors module replaces text with its own text.
Asp.Net/WCF call IHttpResponse::SetStatus with TrySkipCustomErrors
true so that IIS doesn’t override their errors with its own. When
effective errorMode is “Detailed” and response is non-empty, this
value of existingResponse will act as “PassThrough” regardless of
value of TrySkipCustomErrors.
Hopefully this may figure out the essential things when setting custom error page for unhandled exception responses.
MSDN References:
https://msdn.microsoft.com/en-us/library/ms690576(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors(v=vs.110).aspx
SO References:
IIS7 Overrides customErrors when setting Response.StatusCode?
How to send status code "500" for generic error page in IIS?
Getting IIS7 'one liner' error when using custom error settings in web.config
It seems if your iis is set with Integrated Mode then Modules will still be run for static files. If you set it to Classic mode it will ignore the managed modules for static files.
Please refer this thread for more infomration see the answer of João Angelo Requests for static files are hitting the managed code in ASP.NET MVC3
The fix is either you changed your handler to not throw exception or you change iis to classic mode so if any handler or other part throws exception and iis redirects to error.html then none of manage module should hit.
Related
In my global.asax file, I have the following code:
void Application_Error(object sender, EventArgs e)
{
Exception TheError = Server.GetLastError();
if (TheError is HttpException && ((HttpException)TheError).GetHttpCode() == 404)
{
Response.Redirect("~/404.aspx");
}
else
{
Response.Redirect("~/500.aspx");
}
}
When I navigate to an non-existing page, I get the generic error page. I don't want anything pertaining to custom error in the web.config because my plan is to add code to the global.asax file to log the exceptions. Is there a way to handle custom error with just the global.asax file?
EDIT:
The answer is not obvious, but this seems to explain it: http://www.asp.net/web-forms/tutorials/deployment/deploying-web-site-projects/displaying-a-custom-error-page-cs
If you scroll down a ways, you'll find this lovely tidbit:
Note: The custom error page is only displayed when a request is made
to a resource handled by the ASP.NET engine. As we discussed in the
Core Differences Between IIS and the ASP.NET Development Server
tutorial , the web server may handle certain requests itself. By
default, the IIS web server processes requests for static content like
images and HTML files without invoking the ASP.NET engine.
Consequently, if the user requests a non-existent image file they will
get back IIS's default 404 error message rather than ASP.NET's
configured error page.
I've emphasized the first line. I tested this out in the default template, and sure enough, this URL:
http://localhost:49320/fkljflkfjelk
gets you the default IIS page, whereas simply appending a .aspx makes the Application_Error kick in. So, it sounds like you need to enable customErrors/httpErrors if you want to have all errors handled.
For IIS <= 6, add to <system.web>:
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/404.aspx"/>
<error statusCode="500" redirect="/500.aspx"/>
</customErrors>
For IIS7+, add to <system.webServer>:
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/404.aspx" responseMode="ExecuteURL"/>
<remove statusCode="500"/>
<error statusCode="500" path="/500.aspx" responseMode="ExecuteURL"/>
</httpErrors>
I'll leave the original answer in case someone finds it useful.
I believe you need to clear the existing error code from the response, otherwise IIS ignores your redirect in favor of handling the error. There's also a Boolean flag, TrySkipIisCustomErrors, you can set for newer versions of IIS (7+).
So, something like this:
void Application_Error(object sender, EventArgs e)
{
Exception TheError = Server.GetLastError();
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
if (TheError is HttpException && ((HttpException)TheError).GetHttpCode() == 404)
{
Response.Redirect("~/404.aspx");
}
else
{
Response.Redirect("~/500.aspx");
}
}
Looks like IIS is looking for a page and did not find it.
Make sure:
404.aspx is created
customErrors tag from web.config is not present.
I am attempting to catch all errors except '404-File not found' error in global.asax file and writing in to a text file in server,But lot of errors gets missed ,like FormatException Error,Sessionout Error and "Yellow Page of Error" Appears !
How can I catch all errors here.
void Application_Error(object sender, EventArgs e)
{
CT.Bussiness.DevCommonFunctions devTools = new CT.Bussiness.DevCommonFunctions();
HttpContext ctx = HttpContext.Current;
//ctx.Response.
// Get page url
if (ctx != null)
{
string pageName = ctx.Request.Url.ToString();
Exception ex = Server.GetLastError().GetBaseException();
HttpException checkException = (HttpException)ex;
if (checkException.GetHttpCode() != 404)
{
string errorDate = DateTime.Now.ToString();
//string pageName =pageName;
string errorMessage = ex.Message;
string errorSource = ex.Source;
//string errorInnerException=ex.InnerException.ToString();
string errorData = ex.Data.ToString();
string errorTarget = ex.TargetSite.ToString();
string errorStack = ex.StackTrace.ToString();
devTools.WriteErrorsIntoErrorLog(errorDate, pageName, errorMessage, errorSource, "InnerException", errorData, errorTarget, errorStack);
//devTools.SendErrorMail("developer3#devwebservices.net,developer4#devwebservices.net,developer5#devwebservices.net,hr#devwebservices.net", errorDate, pageName, errorMessage, errorSource, "InnerException", errorData, errorTarget, errorStack);
//Response.TrySkipIisCustomErrors = true;
Response.Redirect("~/Error.aspx");
//Response.Redirect("Error.aspx");
}
}
}
SOLVED
I Followed Like this as KPL Adviced below:
Implemented Elmah
Excluded Global.asax and
Wrote redirection code in web.config.
Thus Solved !
Use:
1.) web.config "customErrors" tag to avoid "Yellow Page of Error":
<customErrors mode="On" defaultRedirect="~/Error.aspx">
<error statusCode="400" redirect="/error/400-bad-request/" />
<error statusCode="404" redirect="/error/404-file-not-found/" />
<error statusCode="500" redirect="/error/500-server-error/" />
</customErrors>
2.) Also try using Elmah to logging errors.
ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.
Once ELMAH has been dropped into a running web application and configured appropriately, you get the following facilities without changing a single line of your code:
Logging of nearly all unhandled exceptions.
A web page to remotely view the entire log of recoded exceptions.
A web page to remotely view the full details of any one logged exception, including colored stack traces.
In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrors mode turned off.
An e-mail notification of each error at the time it occurs.
An RSS feed of the last 15 errors from the log.
Im using a CMS product called EPiServer. We need to create our own method of displaying 404's which just can't be achieved using .NET's standard customErrors. We've writen a module which we use to check for the HttpStatusCode. We do this in the EndRequest method.
If the status is 404, we query EPiServer for the appropriate 404 page, and then Transfer the request over to that page. However this doesnt return a 404, and even if I do the following the correct status isnt returned:
HttpContext.Current.Response.StatusCode = 404;
HttpContext.Current.Response.StatusDescription = "Page not Found";
HttpContext.Current.Server.TransferRequest(newPage);
Likewise, if I do a response.redirect instead of a TransferRequest then its not a proper 404 because the url has then changed...
Whats the right way of doing this?
Thanks in advance
Al
Not a direct answer to your question but could also take a look at this open source 404 handler: https://www.coderesort.com/p/epicode/wiki/404Handler
It is also available on episervers nuget feed
Which IIS version are you using? For IIS7 or 7.5 you might need something like this:
<httpErrors errorMode="Custom">
<remove statusCode="404" />
<error statusCode="404" path="/somenotfoundpage.aspx" responseMode="ExecuteURL" />
<remove statusCode="500" />
<error statusCode="500" path="/someerrorpage.aspx" responseMode="ExecuteURL" />
</httpErrors>
You should set the status code in the codebehind of the template you're using for your 404 page.
If this is a plain content page, either create a new template or add a Status Code property to the page and logic in the code behind to send the appropriate header if this is not null or empty.
Try setting the status code on the page that you transfer to - in your error page template. I'm not sure that having a separate module is necessary - you can simply handle the HttpApplication's Error event in Global.asax.cs.
Thanks for your responses.
I actually got this working by doing the following:
In the EndRequest event handler I transferred the request off to the correct EPiServer page, and included a querystring param in the call i.e.
app.Context.Server.TransferRequest(pageUrl + "&internal=true");
Then in the PostRequestHandlerExecute event I check for the querystring param, if it exists it can only be because it's a 404 so I return the correct status:
HttpContext.Current.Response.StatusCode = 404;
HttpContext.Current.Response.StatusDescription = "Page not Found";
Works like a charm.
Thanks
higgsy
In asp.net, I can define a custom error page like this:
<configuration>
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="/servererrors/404.aspx" />
</customErrors>
</system.web>
</configuration>
Now my question: If I replace, say 404.aspx with AnyHTTP.aspx,
and want to get the number of the http error to generalize the page, how do I get that error numer?
Try this setting in CustomErrors (ASP.NET 3.5 SP1):
<customErrors mode="RemoteOnly" defaultRedirect="/servererrors/AnyHTTP.aspx" RedirectMode="ResponseRewrite"/>
As a different solution, you can also do this in Global.asax:
void Application_Error(object sender, EventArgs e)
{
Server.Transfer("/servererrors/AnyHTTP.aspx");
}
and on your error page, load the last error:
Exception e = Server.GetLastError();
It is important to use Server.Transfer() in the Global.asax file; using Response.Redirect will throw a 302 error and you will lose the error that you wanted to catch.
Well you might take a look at http://www.raboof.com/projects/Elmah/ before you venture to deep into doing your own thing...
I'd recommend not using the web.config method. customErrors redirects to the error page, which makes little sense. Essentially it first says "oh yes, that'll work perfectly, you just need to go here instead", and then says "oh, we didn't find that". That's really a bug (if there isn't anything here, then why did the server tell me to go here, clearly to the user code it looks like you the server code messed up; they went to the right URI and then you directed them to the wrong one).
Use Server.Transfer() from global.asax, set a default HTTPHandler, or set IIS to execute (not redirect to) your .aspx or other file with your implementation. If you want the same handler to manage each error, then you could, for example, do a Server.Transfer() from global.asax, but include a query string parameter about the type of error (whether simply an HTTP status code, or something more detailed), or pass information in the HttpContext.
Coming from a desktop background I'm not sure exactly how to pass the exceptions I have caught to an Error page in order to avoid the standard exception screen being seen by my users.
My general question is how do I pass the exception from page X to my Error page in ASP.net?
I suggest using the customErrors section in the web.config:
<customErrors mode="RemoteOnly" defaultRedirect="/error.html">
<error statusCode="403" redirect="/accessdenied.html" />
<error statusCode="404" redirect="/pagenotfound.html" />
</customErrors>
And then using ELMAH to email and/or log the error.
The pattern I use is to log the error in a try/catch block (using log4net), then do a response.redirect to a simple error page. This assumes you don't need to show any error details.
If you need the exception details on a separate page, you might want to look at Server.GetLastError. I use that in global.asax (in the Application_Error event) to log unhandled exceptions and redirect to an error page.
We've had good luck capturing exceptions in the Global.asax Application_Error event, storing them in session, and redirecting to our error page. Alternately you could encode the error message and pass it to the error page in the querystring.
You can also get the exception from
Server.GetLastError();
Use the custom error pages in asp.net, you can find it in the customError section in the web.config
We capture the exception in the Global.asax file, store it in Session, the user is then redirected to the Error Page where we grab the exception for our Session variable and display the Message information to the user.
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
this.Session[CacheProvider.ToCacheKey(CacheKeys.LastError)] = ex;
}
We do log the error message prior to displaying it the user.
I think you can use the global.asax -- Application_Exception handler to catch the exception and then store it for displaying in an error page.
But actually, your error page shouldn't contains code that might cause just another error. It should be simple "Oops! something went wrong" page.
If you want details on the error, use Windows' events viewer or ELMAH or employ some logging mechanism.