I have custom error handling defined for my application and it all works wonderfully - when a resource cannot be found, the correct 'NotFound' view renders. When an unhanlded exception occurs, the 'ServerError' view renders.
The problem I am facing is that my application insists on trying to find a View called 'Error' but doesn't find it, as i don't have one and thus this exception gets thrown during my custom error handling routine:
"The view 'Error' or its master was not found or no view engine supports the searched locations. The following locations were searched: ... "
I have an Application_Error event handler which does the logging of all unhandled exceptions:
protected void Application_Error(Object sender, EventArgs e)
{
Exception lastEx = Server.GetLastError();
// Attempt to Log the error
try
{
Utils.Logger.Error(lastEx);
}
catch (Exception loggingEx)
{
// Try and write the original ex to the Trace
Utils.TryTrace(lastEx.Message);
// Try and write the logging exception to the Trace
Utils.TryTrace(loggingEx.Message);
}
}
I have customErrors turned 'On' in my web.config:
<customErrors mode="On" defaultRedirect="blah">
<error statusCode="404" redirect="dee"/>
<error statusCode="500" redirect="blah"/>
</customErrors>
And i have routes defined in my Global.asax.cs RegisterRoutes method which correspond to the Redirect defined in web.config above:
routes.MapRoute(
"Error - 404",
"dee",
new { controller = "Error", action = "NotFound" }
);
routes.MapRoute(
"ServerError", // When this route is matched we want minimal error info displayed
"blah",
new { controller = "Error", action = "ServerError" }
);
I have a BaseController which contains an OnActionExecuted routine:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
Logger.Info(String.Format(Constants.LOG_ACTION_EXECUTED, filterContext.Controller.ToString(), filterContext.ActionDescriptor.ActionName));
// Log any exceptions
if (filterContext.Exception != null)
{
Stack<string> messages = new Stack<string>();
Exception current = filterContext.Exception;
messages.Push(current.Message);
while (current.InnerException != null)
{
messages.Push(current.InnerException.Message);
current = current.InnerException;
}
StringBuilder result = new StringBuilder();
while (messages.Count != 0)
{
result.Append(messages.Pop());
string nextline = messages.Count > 0 ? "OUTER EXCEPTION " + messages.Count + ": " : "";
result.Append(Environment.NewLine);
}
Logger.Error(result.ToString());
}
base.OnActionExecuted(filterContext);
}
Is there somewhere else that the Framework defines which view to render in the event of an unhandled exception?
Is my custom error handling routine missing a final step which would ensure that the Framework no longer expects to find the 'Error' view???
You need to remove the HandleErrorAttribute handler in Global.asax.cs file. This attribute sets the view as Error. Then MVC runtime will not handle the exception and exception will propagate to Asp.Net runtime where it will use the customErrors section to display the page.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute()); // remove this line
}
I had already removed the line which added the filter automatically to all Controllers...as suggested by Eranga - so that wasn't what was causing the Framework to search for the 'Error' view.
The problem i was experiencing was being caused by some left over [HandleError] attribute tags sitting on top of one of my Controllers.
So it is interesting to note that: despite the fact that my controller had the [HandleError] attribute decorated at the top of its class definition, my custom error handling routine, as defined in the web.config, was still being called - and was correctly rendering the required Views...
The framework error handling (as defined by the HandleErrorAttribute) would fail, my Application_Error would catch the Exception and silently log it to the Database via my 'Logger' instance...then my custom error handling routine would complete successfully.
Related
Can someone give me a working example of an ExceptionHandler which extends ExceptionHandlerWrapper for both normal and ajax request. I don't want to use a filter. I want all my exception handling to be in one entry place for logging purpose.
In Omnifaces we have the FullAjaxExceptionHandler but only for ajax request. How can I refactor this class in order to take into account both types of request?
In order to render the error page inside my CustomExceptionHandler, I use inside a utility method such a statement which identify the type of request (normal or ajax)
if (context.getPartialViewContext().isAjaxRequest()) {
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, errorPageLocation);
context.setViewRoot(viewRoot);
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
} else {
NavigationHandler nav = context.getApplication().getNavigationHandler();
nav.handleNavigation(context, null, errorPageLocation);
context.renderResponse();
}
By this way I can handle both request effectively in my CustomExceptionHandler
In the Wrapper you can use, if you want to detect the type of request:
context == null || !context.getPartialViewContext().isAjaxRequest()
Also recommend you to integrate the WrapperHandler with Deltaspike Exception Control
And finally a common way to redirect (working for both types of requests):
public static void redirect(FacesContext fc, String view) {
ExternalContext ec = fc.getExternalContext();
String path = ec.encodeResourceURL(fc.getApplication().getViewHandler().getActionURL(fc, view));
try {
ec.redirect(path);
} catch(Exception e) {
throw new RuntimeException();
}
}
I need some Elmah logging in a async task executing on my webserver. But when I try to log the error it fails because of the HttpContext.
var httpContext = HttpContext.Current;
Task.Factory.StartNew(() =>
{
HttpContext.Current = httpContext;
try
{
//Execute some code
}
catch (Exception ex)
{
//Generate some error for the user and log the error in Elmah
try
{
ErrorLog.GetDefault(HttpContext.Current).Log(new Error(ex));
}
catch(Exception ex)
{
}
}
});
To get the progress for the task I implemented some polling mechanism. Currently none of the errors are logged to Elmah which make it difficult to solve them.
Also providing the context as parameter doesn't work.
It doesn't work. I get an ArgumentException telling me the expected value doesn't fall within the expected range. With the following stacktrace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Web.Hosting.IIS7WorkerRequest.GetServerVariableInternal(String name)
at System.Web.HttpRequest.AddServerVariableToCollection(String name)
at System.Web.HttpRequest.FillInServerVariablesCollection()
at System.Web.HttpServerVarsCollection.Populate()
at System.Web.HttpServerVarsCollection.Get(String name)
at Elmah.ErrorLog.InferApplicationName(HttpContext context)
at Elmah.ErrorLog.GetDefaultImpl(HttpContext context)
at Elmah.ServiceContainer.GetService(Type serviceType)
at Elmah.ServiceCenter.GetService(Object context, Type serviceType)
at Elmah.ErrorLog.GetDefault(HttpContext context)
at Bis.Utilities.Log.ElmahErrorLog.TryLogError(Exception exeption) in D:\Users\A500535\Documents\Projecten\Biobank\Bis\src\Utilities\Log\ElmahErrorLog.cs:line 13
Below is one ugly hack that might get the job done. Essentially, it creates an Error object on a bogus Exception (the prototype) so that the context can be captured while the request is still in flight. Later, when the task started as a result of the request fails, another Error object is created off the actual exception that occurred and then the interesting and contextual bits are selectively copied off the earlier prototype. Unfortunately, the prototype Error has to be created whether or not an exception will occur.
// Create an error that will capture the context
// and serve as a prototype in case a real exception
// needs logging
var prototype = new Error(new Exception(), context);
Task.Factory.StartNew(() =>
{
try
{
// Execute some code
}
catch (Exception ex)
{
// Generate some error for the user and log the error in ELMAH
try
{
// Create a new error without contextual information
// but then copy over the interesting bits from the
// prototype capture at time of request.
var error = new Error(ex)
{
HostName = prototype.HostName,
User = prototype.User,
};
error.ServerVariables.Add(prototype.ServerVariables);
error.QueryString.Add(prototype.QueryString);
error.Cookies.Add(prototype.Cookies);
error.Form.Add(prototype.Form);
ErrorLog.GetDefault(null).Log(error);
}
catch(Exception)
{
}
}
});
When you start a new thread it doesn't get the HttpContext structure. Since Elmah logging requires the HttpContext data, it will fail.
See the following QA:
Elmah Does not email in a fire and forget scenario
for me this worked in a async task called with Task.Run:
Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(new NotSupportedException("elmah logging test")));
I'm working on a team-project and I am in the following situation:
I created my own Exception class, and I want all the thrown exceptions of type myException to be handled and automatically redirected to the Error view where I would nicely display the error, which is ok to do. This is what I added in my Web.config:
<customErrors mode="On" defaultRedirect="Error" />
The issue is I want all the rest of the exceptions to be thrown normally, seeing all the information about it, including the stack trace, the source file and the line error, which would be really good for the team-project.
I've tried the [HandleError(ExceptionType=typeof(myException)], but it is no use.
I also tried to override the OnException function of the controller and if the exception is not myException then i would throw it again, but i still get in the Error view.
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() != typeof(myException)) {
throw filterContext.Exception;
}
base.OnException(filterContext);
}
Any idea which could work?
Thanks.
You may get the result you want by leaving custom errors Off (so that for all the errors you get the stack trace displayed), and redirecting the exceptions you want to the controller/view you need (so that a friendly-looking page will be displayed).
You could define a base controller for all your controllers, and override its OnException method with something like below:
if (filterContext.Exception.GetType() == typeof(YourCustomException))
{
filterContext.ExceptionHandled = true;
filterContext.Result = RedirectToAction("ActionName", "ControllerName", new { customMessage = "You may want to pass a custom error message, or any other parameters here"});
}
else
{
base.OnException(filterContext);
}
This must be simple and already answered, but I've wasted many hours on it. I can't figure how to get an error page on mistyped address. Also I'd prefer not to redirect, but to keep the URL. I've tried many combinations of CustomErrors, HttpErrors and Application_Error, but nothing works for non-existent controller - depending on HttpErrors I always get IIS 404.0 page or just an empty 404 response. Running on IIS 7.5, MVC 3.
I don't remember where I got the solution. But here is the code to handle the error:
First, you create a ErrorController:
public class ErrorController : Controller
{
//
// GET: /Error/
public ActionResult Index()
{
return RedirectToAction("Index", "Home");
}
public ActionResult Generic()
{
Exception ex = null;
try
{
ex = (Exception)HttpContext.Application[Request.UserHostAddress.ToString()];
}
catch { }
return View();
}
public ActionResult Error404()
{
return View();
}
}
Second, open Global file and add the following code:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
Application[HttpContext.Current.Request.UserHostAddress.ToString()] = ex;
}
Third, change customerror in your webconfig:
<customErrors mode="Off" defaultRedirect="/Error/Generic">
<error statusCode="404" redirect="/Error/Error404"/>
</customErrors>
More: I created one more error layout. It makes things even more clear. :)
Hope this helps you.
I use the following route to ensure all requests not matching any other route fall there, then you can handle that case very easily:
// this route is intended to catch 404 Not Found errors instead of bubbling them all the way up to IIS.
routes.MapRoute(
"PageNotFound",
"{*catchall}",
new { controller = "Error", action = "NotFound" }
);
Map that last (include that statement after any other .MapRoute statements).
Problem: How to display exception detail (InnerException?) in my DbError view in MVC3
The code below produces a null reference when trying to display the InnerException.
<p>#Model.Exception.GetType().Name<br />
#Model.Exception.InnerException.Message.ToString()<br /> <!-- null reference! -->
thrown in #Model.ControllerName #Model.ActionName</p>
In my global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute
{
ExceptionType = typeof(DbException),
// DbError.cshtml is a view in the Shared folder.
View = "DbError",
Order = 2
});
code originally from http://community.codesmithtools.com/CodeSmith_Community/b/tdupont/archive/2011/03/01/error-handling-and-customerrors-and-mvc3-oh-my.aspx
Not all exceptions have an inner one. Make sure that the provided exception actually has an InnerException. If it doesn't, simply print out the details of the provided exception instead (which you generally should do, the InnerException simply details the error mentioned in the root exception).