Is it possible to configure a "catch all" custom error page within Umbraco that will display if there's an unhandled exception thrown somewhere within a site?
Question: is this solely using .net functionality, or is their an umbraco handler dealing with this.
Right now, it's throwing a 302: Temporary Redirect. Technically, this isn't correct since it should be throwing a 500. The page would then display the page instead of the requested page. Similar to how the regular 404 is currently handling.
Just curious if this is worth my time investigating, or if it's 'just how it works' and I should just deal.
I am in need of a solution for 500 errors as well. We need to change the 500 server response to a 404 server response. Can someone please show me where Umbraco is handling its application error handling so that I can add a 500 function to it.
void Context_Error(object sender, EventArgs e) { var Response = HttpContext.Current.Response; var Server = HttpContext.Current.Server;
if (/*check we're not already on error page, and perhaps check if we're remoteOnly errors.*/) { Exception exception = Server.GetLastError().GetBaseException(); Response.StatusDescription = exception.Message;
if (/*test for certain types of exceptions which match differnt response codes*/) { Response.StatusCode = (int)HttpStatusCode.Forbidden; Response.StatusDescription = exception.Message; Server.Transfer("/Error.aspx"); } /*..more exception types..*/ else { Response.StatusCode = (int)HttpStatusCode.InternalServerError; Server.Transfer("/Error.aspx"); } } }
public void Dispose() { //nothing to clean up. } }
Murray's answer worked for me. I wanted to place the class in a helper assembly I wrote, so I thought for the benefit of anyone else who wants to try it, I'd mention that I had to register the module twice:
One other thing to mention is that I wanted to use a node as my error page. Server.Transfer didn't work for that, presumably because the "page" I was transferring to doesn't exist as a file on the server. Fortunately I found a workaround for that, at http://our.umbraco.org/forum/using/ui-questions/3862-Usercontrol-Servertransfer?p=0
@Nico, one thing you need to be aware of, is that your solution does not return the correct HTTP response code.
I've actually simplified my solution since writing the above to 'mostly' use the built in machanism with just a handler to correct the http response codes.
@Andy, the below solution is not designed work with content from the CMS, I wanted to avoid that because if some kind of CMS problem is causing the exception you will get unexpected results.
So first setup errors similar to nico's suggestion like so, with the important difference being redirectMode="ResponseRewrite" this will ensure the HTTP response code is not 302 (which is just wrong: I'm talking to you @microsoft)
The other part is the handler to ensure the correct HTTP response codes: (and catch a few edge cases)
class FixHttpStatusCodes : IHttpModule
{
private string defaultRedirect = null;
public void Init(HttpApplication context)
{
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~/");
CustomErrorsSection section = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");
if (section != null && section.DefaultRedirect != null)
defaultRedirect = section.DefaultRedirect;
context.Error += Context_Error;
}
void Context_Error(object sender, EventArgs e)
{
var Response = HttpContext.Current.Response;
var Server = HttpContext.Current.Server;
Exception exception = Server.GetLastError().GetBaseException();
Response.StatusDescription = exception.Message;
if (exception is HttpRequestValidationException)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
else if (exception is HttpException && Regex.IsMatch(exception.Message, @"^The file '[^']*' does not exist\.$"))
Response.StatusCode = (int)HttpStatusCode.NotFound;
else if (exception is HttpException && Regex.IsMatch(exception.Message, @"^Path '[^']*' is forbidden\.$"))
{
Response.StatusCode = (int)HttpStatusCode.Forbidden;
Response.StatusDescription = exception.Message;
}
else if (exception is HttpException && exception.Message.StartsWith("Maximum request length exceeded."))
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~/");
HttpRuntimeSection section = (HttpRuntimeSection)configuration.GetSection("system.web/httpRuntime");
Response.StatusDescription = string.Format("Uploads must be less than {0:n0}KB.", section.MaxRequestLength);
if(!string.IsNullOrEmpty(defaultRedirect))
Server.Transfer(defaultRedirect); // need to transfer for this one.
}
else if (exception is FileNotFoundException && exception.Message.IndexOf("Could not find file") == -1) // page not found exceptions don't include the text "Could not find file"
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
else
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
public void Dispose()
{
//nothing to clean up
}
}
Then you register the handler as shown by Andy's message.
I implemented and registered the above code (httpModule)
and
I created a control in usercontrols/Test_WebUserControl.ascx and intentionally I wrote some incorrect code in the control.
Httpmodule does not redirect to error page neither it record error to database table [umbracoLog] instead it gives me error!
Error creating control (usercontrols/Test_WebUserControl.ascx). Maybe file doesn't exists or the usercontrol has a cache directive, which is not allowed! See the tracestack for more information!
Please could you advise, do I need to follow different steps to catch error in usercontrol.
Does Umbraco.Log store application errors that occur through IIS or unhandled exceptions?
Edit: It would appear not. Is there any way to handle application errors through a BaseActionInvoker or similar methods used by MVC? I've seen applications that use a BaseActionInvoker on a base controller class to encapsulate actions within a try/catch loop so that exception details can be logged to a database. However, being that umbraco uses its own routing system outside of standard MVC, I'm not sure if a similar approach is possible.
So a BaseActionInvoker was the not solution in my environment. Instead, I added an OnException event to a base surface controller and inherited that controller on all my umbraco controllers. This enabled the ability to log all application errors. Other errors are handled through the umbraco 404 page.
protected override void OnException(ExceptionContext filterContext) { Exception e = filterContext.Exception; //Log Exception e filterContext.ExceptionHandled = true; //Add code to log exception filterContext.Result = new ViewResult() { ViewName = "Error" }; }
Custom Error Page
Is it possible to configure a "catch all" custom error page within Umbraco that will display if there's an unhandled exception thrown somewhere within a site?
- Mark
Yes its very easy:
For a 404: just make one page in umbraco and set its nodeid in the error404 tag in the /config/umbracoSettings.config file
For a 500: you can put the fallback in the customError tag in the web.config
Like this:
Question: is this solely using .net functionality, or is their an umbraco handler dealing with this.
Right now, it's throwing a 302: Temporary Redirect. Technically, this isn't correct since it should be throwing a 500. The page would then display the page instead of the requested page. Similar to how the regular 404 is currently handling.
Just curious if this is worth my time investigating, or if it's 'just how it works' and I should just deal.
-C
I am in need of a solution for 500 errors as well. We need to change the 500 server response to a 404 server response. Can someone please show me where Umbraco is handling its application error handling so that I can add a 500 function to it.
Bump!
How can you configure a custom 500 that responds with a 500 status code rather than the default ASP.NET 302
One way is to write your own Error Handling Module, something like
class ErrorHandlingModule : IHttpModule
{
public void Init(HttpApplication context)
{
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~/");
CustomErrorsSection section = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");
if(section.Mode != CustomErrorsMode.Off)
context.Error += Context_Error;
}
void Context_Error(object sender, EventArgs e)
{
var Response = HttpContext.Current.Response;
var Server = HttpContext.Current.Server;
if (/*check we're not already on error page, and perhaps check if we're remoteOnly errors.*/)
{
Exception exception = Server.GetLastError().GetBaseException();
Response.StatusDescription = exception.Message;
if (/*test for certain types of exceptions which match differnt response codes*/)
{
Response.StatusCode = (int)HttpStatusCode.Forbidden;
Response.StatusDescription = exception.Message;
Server.Transfer("/Error.aspx");
}
/*..more exception types..*/
else
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
Server.Transfer("/Error.aspx");
}
}
}
public void Dispose()
{
//nothing to clean up.
}
}
Murray's answer worked for me. I wanted to place the class in a helper assembly I wrote, so I thought for the benefit of anyone else who wants to try it, I'd mention that I had to register the module twice:
<system.web>
<httpModules>
<add name="ErrorHandlingModule" type="HelperAssembly.ErrorHandlingModule, HelperAssembly" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorHandlingModule" type="HelperAssembly.ErrorHandlingModule, HelperAssembly" />
</modules>
</system.webServer>
One other thing to mention is that I wanted to use a node as my error page. Server.Transfer didn't work for that, presumably because the "page" I was transferring to doesn't exist as a file on the server. Fortunately I found a workaround for that, at http://our.umbraco.org/forum/using/ui-questions/3862-Usercontrol-Servertransfer?p=0
Worked great once I did that! Thanks!
@Nico, one thing you need to be aware of, is that your solution does not return the correct HTTP response code.
I've actually simplified my solution since writing the above to 'mostly' use the built in machanism with just a handler to correct the http response codes.
@Andy, the below solution is not designed work with content from the CMS, I wanted to avoid that because if some kind of CMS problem is causing the exception you will get unexpected results.
So first setup errors similar to nico's suggestion like so, with the important difference being redirectMode="ResponseRewrite" this will ensure the HTTP response code is not 302 (which is just wrong: I'm talking to you @microsoft)
The other part is the handler to ensure the correct HTTP response codes: (and catch a few edge cases)
Then you register the handler as shown by Andy's message.
Hi Murray and Andy,
I implemented and registered the above code (httpModule)
and
I created a control in usercontrols/Test_WebUserControl.ascx and intentionally I wrote some incorrect code in the control.
Httpmodule does not redirect to error page neither it record error to database table [umbracoLog] instead it gives me error!
Maybe file doesn't exists or the usercontrol has a cache directive, which is not allowed! See the tracestack for more information!
Please could you advise, do I need to follow different steps to catch error in usercontrol.
kind regards
saif
Why not extend Application_Error in Global.asax to do your bidding ?
Does Umbraco.Log store application errors that occur through IIS or unhandled exceptions?
Edit: It would appear not. Is there any way to handle application errors through a BaseActionInvoker or similar methods used by MVC? I've seen applications that use a BaseActionInvoker on a base controller class to encapsulate actions within a try/catch loop so that exception details can be logged to a database. However, being that umbraco uses its own routing system outside of standard MVC, I'm not sure if a similar approach is possible.
So a BaseActionInvoker was the not solution in my environment. Instead, I added an OnException event to a base surface controller and inherited that controller on all my umbraco controllers. This enabled the ability to log all application errors. Other errors are handled through the umbraco 404 page.
protected override void OnException(ExceptionContext filterContext)
{ Exception e = filterContext.Exception;
//Log Exception e filterContext.ExceptionHandled = true;
//Add code to log exception
filterContext.Result = new ViewResult() { ViewName = "Error" };
}
is working on a reply...