Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalFilters.Filters.Add(new HandleErrorAttribute()); // i added this
}
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null
{
string action;
switch (httpException.GetHttpCode())
{
case 404:
// page not found
action = "HttpError404";
break;
case 500:
// server error
action = "HttpError500";
break;
default:
action = "General";
break;
}
}
// clear error on server
Server.ClearError();
//return new EmptyResult();
}
Action:
public ActionResult Index()
{
var a = Convert.ToInt64(""); // I get exception after that project is not running
return view();
}
Question:
I'm trying to use dynamic exception in asp.net MVC. In order to do this, I added a method in global.asax.cs. Exception handling works, however project does not run after exception occurs.
When I get an exception, I want the project to keep running, like when using a try-catch statement, However when I get the exception, the project stops working.
What to add or what to change in order for the project to continue running?
Summary:
Application_Error does not handle exceptions of the type your code is throwing and even if it did you don't return anything.
Detail:
You are trying to use two different aspects of exception handling in ASP.NET MVC.
If you register the HandlerErrorAttribute in GlobalFilters then you are saying that for any uncaught error you want to redirect to the Error page of the Application, which by default will be found in the /Views/SharedFolder.
But this only works if customErrors="On" in web.config:
<system.web>
<customErrors mode="On" />
</system.web>
Note that you can also apply HandlerErrorAttribute at the Controller or ActionMethod level rather than globally.
If customErrors="On" but you do not have an Error page defined in /Views/SharedFolder then it will throw a compounded error of type System.InvalidOperationException which in turn will bubble up to Application_Error.
If on the other hand, customErrors="Off" then the HandleErrorAttribute mechanics won't fire and instead the exception triggered by your Index ActionMethod will bubble up to the GlobalError handler you have defined in Application_Error.
In this case the exception will relate to your code:
var a = Convert.ToInt64("");
This will throw an exception of type System.InvalidFormatException.
So if you set a breakpoint in Application_Error you will see that this method does run but it wont actually do anything because your switch statements are only assuming an httpException:
HttpException httpException = exception as HttpException;
if (httpException != null)
httpException will always be null under these circumstances because neither System.InvalidOperationException or System.InvalidFormatException inherit from HttpException.
So you need to be doing something more like:
HttpException httpException = exception as HttpException;
if (httpException == null)
{
// General exception handling logic here
}
else
{
// Http exception handling switch statement here
}
That said even if you properly catch and handle the error you're not doing anything with it or after it:
//return new EmptyResult();
So you will still get a blank page. You should do a Redirect or a Server.Transfer or something at this point.
When an exception is raised, it is passed through the call stack until it is caught.
If it is not caught, then the application stops running.
Here you don't try to catch your exception, that's why your application stops running.
All you need is that:
public ActionResult Index()
{
try
{
var a = Convert.ToInt64("");
}
catch (YourExceptionType ex)
{
// Do something
}
return view();
}
But you also can use the TryParse method, which would avoid using the exception mechanism, which is quite a heavy thing.
Also remember that catching an exception means you know what to do to keep your application in a stable state, even if that exception is raised.
Related
I log errors in my Actions using NLog to store errors with additional information, for example:
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x;
return RedirectToAction(MVC.Client.Index());
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
}
}
And I have Error handling configured in Web.config:
<customErrors mode="On" />
But I don't get redirected to the Error.cshtml when I execute the Action (the page remains in the same place), why?
Can I use Elmah to do the same thing? (logging additional information like client Id)
First of all, most people solve this error by not catching the exception. This way, the exception propagates to ASP.NET, which displays a "500 Internal Error" webpage, and all the pertinent information is logged.
If your server is configured for production, the error page will just say "an error occurred, details were logged."
If the server is configured for development, then you will get the famous yellow page with the exception type, the message, and the stack trace.
Swallowing the exception and manually redirecting to an error page is a bad practice because it hides errors. There are tools that examine your logs and give you nice statistics, for example about percentages of successful/failed requests, and these won't work any more.
So, not swallowing the exception is what people do, and at the very least, it solves your problem.
Now, I find this very clunky, because I do not like manually looking for the source files mentioned in the yellow page and manually going to the mentioned line numbers. I practically have no use for the yellow page, it might just as well just say "an error occurred, cry me a river, nah-nah-nah." I don't read the yellow page.
Instead, I do like to log exceptions on my own, and I have my logger begin each line with full-path-to-source-filename(line):, so that every line on the debug log in visual studio is clickable, and clicking on a line automatically opens up the right source file, and scrolls to the exact line that issued the log message. If you want this luxury, then go ahead and catch the exception, but right after logging the exception you have to rethrow it, so that things can follow their normal course.
Amendment
Here is some information that was added in comments:
So, you can do the following:
try
{
...
}
catch (Exception e)
{
log( "information" );
throw; //special syntax which preserves original stack trace
}
Or
try
{
...
}
catch (Exception e)
{
throw new Exception( "information", e ); //also preserves original stack trace
}
Do not do this: catch( Exception e ) { log( "information" ); throw e; } because it loses the original stack trace information of e.
In your code, error occur at the division portion(x/=x) so no execution of redirect line(index page) and jump to catch portion executing the logger. You have to define the redirect to Error.cshtml in catch portion also.
Note: when you use try catch block error will not occur at ASP.NET level resulting no redirect to Error.cshtml page
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x; /// error occur here
return RedirectToAction(MVC.Client.Index()); /// no execution of this line
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
/// add redirect link here
return RedirectToAction(MVC.Client.Error()); /// this is needed since the catch block execute mean no error at ASP.net level resulting no redirect to default error page
}
}
This will streamline your exception handling and allow you to manage the process more succinctly. Create an attribute like this:
public class HandleExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
// Pass in necessary data, etc
private string _data;
public string Data
{
get { return _data; }
set { _data = value; }
}
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// Logging code here
// Do something with the passed-in properties (Data in this code)
// Use the filterContext to retrieve all sorts of info about the request
// Direct the user
base.OnException(filterContext);
}
}
Now you can use it on a controller or method level with an attribute like this:
[HandleException(Data="SomeValue", View="Error")]
Or, register it globally (global.asax) like this:
GlobalFilters.Filters.Add(new HandleExceptionAttribute());
I've create new attribute and in the logic I've some exception
My question is: how should I handle it in the controller and pass it to the view?
In the code below I throw the exception, how should I move it to the view?
This is the attribute in the controller
[HttpPost]
[CheckToken]
public JsonResult Edit(Roles role)
{
...
}
This is the attribute
public class CheckToken : FilterAttribute, IAuthorizationFilter
{
....
catch (HttpAntiForgeryException e)
{
throw new HttpAntiForgeryException("token not found");
}
}
From the book CLR Via C#, there is an advice on exceptions handling:
Don’t CatchEverything
A ubiquitous mistake made by developers who
have not been properly trained on the proper use of
exceptions is to use catchblocks too often and
improperly. When you catch an exception, you’re stating that you
expected this exception, you understand why it occurred, and you know
how to deal with it.
We should catch exceptions only when we know how to recover our application state from that exception.
In your code, you catch the exception without doing anything than just re-throwing it. This is just not necessary. And be aware that when you re-throw the exception, the CLR resets its starting point for the exception.
catch (HttpAntiForgeryException e)
{
throw new HttpAntiForgeryException("token not found"); // CLR thinks this is where exception originated.
}
In your case, I would not handle the exception and pass it to the view. I will just let the execution stop and handle it inside a global exception filter to return an error to the user with correct http status code (for example: we should not return an error page with status 200) and may optionally log an error to DB for further analysis.
For information how to implement global exception filter: http://forums.asp.net/t/1848242.aspx?How+to+implement+global+error+handling+in+ASP+NET+Web+API
Instead of throwing exception..u can do like as :-
catch (HttpAntiForgeryException e)
{
filterContext.RouteData.Values.Add("Antiforgery", "token not found");
}
and use Routevalues in ur jsonresult as :
[HttpPost]
[CheckToken]
public JsonResult GroupEdit(Roles role)
{
ViewData["Message"] = RouteData.Values["Antiforgery"];
}
I have setup so that if an Exception is thrown I can display it with my custom error page. But in some cases I don't want to be navigated to the error page, but want it to display a simple dialog window.
public ActionResult Page1()
{
//The custom error page shows the exception, if one was thrown
throw new Exception( "An exception was thrown" );
return View();
}
public ActionResult Page2()
{
//A dialog should show the exception, if one was thrown
try
{
throw new Exception( "An exception was thrown" );
}
catch( Exception ex )
{
ViewData["exception"] = ex;
}
return View();
}
Is it possible to have a CustomAttribute to handle an exception which has been thrown in an Controller action? If I added CatchException to Page2, can I automate the process of storing the exception in the ViewData, each time an exception was thrown. I don't have much experience of CustomAttributes and I'd be much appreciated if you could help me.
The Page2 example works perfectly fine, I just want to make the code cleaner as it isn't really pretty to have try catches in every action (where I want to show a dialog).
I am using .NET MVC 4.
You can create a base controller that catch the exceptions and handle it for you.
Also, looks like the Controllers already have a mechanism to do that for you. You'll have to override the OnException method inside the controller. You can get a good example here:
Handling exception in ASP.NET MVC
Also, there's another answer on how to use the OnException here:
Using the OnException
By using that, your code will be cleaner, since you will not be doing a lot of try/catch blocks.
You'll have to filter the exception you wanna handle. Like this:
protected override void OnException(ExceptionContext contextFilter)
{
// Here you test if the exception is what you are expecting
if (contextFilter.Exception is YourExpectedException)
{
// Switch to an error view
...
}
//Also, if you want to handle the exception based on the action called, you can do this:
string actionName = contextFilter.RouteData.Values["action"];
//If you also want the controller name (not needed in this case, but adding for knowledge)
string controllerName = contextFilter.RouteData.Values["controller"];
string[] actionsToHandle = {"ActionA", "ActionB", "ActionC" };
if (actionsTohandle.Contains(actionName))
{
//Do your handling.
}
//Otherwise, let the base OnException method handle it.
base.OnException(contextFilter);
}
You can create subclass of Exception class, and catch it in your Page 2
internal class DialogException : Exception
{}
public ActionResult Page2()
{
//This should a dialog if an exception was thrown
try
{
//throw new Exception( "An exception was thrown, redirect" );
throw new DialogException( "An exception was thrown, show dialog" );
}
catch( DialogException ex )
{
ViewData["exception"] = ex;
}
return View();
}
I'm using C# to build a service operation. When something goes wrong, I want to throw an exception that could be catch client side.
However, when an exception is thrown the client is only able to get a generic error like "400: bad request" and the exception message is not accessible.
In my service operation, I have enabled verbose errors with this:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
and
config.useVerboseErrors = true;
I also unpack the TargetInvocationException and instead return a DataServiceException with this function:
protected override void HandleException(HandleExceptionArgs args)
{
// Handle exceptions raised in service operations.
if (args.Exception.GetType() == typeof(TargetInvocationException)
&& args.Exception.InnerException != null)
{
if (args.Exception.InnerException.GetType() == typeof(DataServiceException))
{
// Unpack the DataServiceException.
args.UseVerboseErrors = true;
args.Exception = args.Exception.InnerException as DataServiceException;
}
else
{
// Return a new DataServiceException as "400: bad request."
args.UseVerboseErrors = true;
args.Exception = new DataServiceException(400, args.Exception.InnerException.Message);
}
}
}
When I use the browser, I can see the verbose exception message, but when I try programmatically, the inner exception is null and I only see the generic error message "400: bad request".
Strangely, if I return a code 200 instead of 400, I can see the exception message in the answer body. But obviously I don't want to do this.
So, is there a way to get the exception message client side, when you throw an exception from a service operation?
Have you had a look at end to end tracing? Furthermore, this MSDN page isn't as daunting as it may first seem, and i think the "Provide Additional Information When an Exception Occurs" section would be useful to you. Have a lovely read.
I am using ELMAH to log unhandled exceptions in an ASP.NET Webforms application. Logging is working fine.
I want to pass the ELMAH error log id to a custom error page that will give the user the ability to email an administrator about the error. I have followed the advice from this answer. Here is my global.asax code:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
Session[StateKeys.ElmahLogId] = args.Entry.Id;
// this doesn't work either:
// HttpContext.Current.Items[StateKeys.ElmahLogId] = args.Entry.Id;
}
But, on the Custom error page, the session variable reference and HttpContext.Current.Items are giving me a NullReference exception. How can I pass the ID to my custom error page?
This works for me:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
if (args.Entry.Error.Exception is HandledElmahException)
return;
var config = WebConfigurationManager.OpenWebConfiguration("~");
var customErrorsSection = (CustomErrorsSection)config.GetSection("system.web/customErrors");
if (customErrorsSection != null)
{
switch (customErrorsSection.Mode)
{
case CustomErrorsMode.Off:
break;
case CustomErrorsMode.On:
FriendlyErrorTransfer(args.Entry.Id, customErrorsSection.DefaultRedirect);
break;
case CustomErrorsMode.RemoteOnly:
if (!HttpContext.Current.Request.IsLocal)
FriendlyErrorTransfer(args.Entry.Id, customErrorsSection.DefaultRedirect);
break;
default:
break;
}
}
}
void FriendlyErrorTransfer(string emlahId, string url)
{
Server.Transfer(String.Format("{0}?id={1}", url, Server.UrlEncode(emlahId)));
}
Unable to comment on Ronnie's solution. I had that in place for a while but it breaks the standard error flow process and causes ErrorLog_Logged to always transfer, even when calling
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
This is a problem if you still want to log an error from within a catch statement, for instance you have a workaround for an error but want to log the error to perform a proper fix, which can be pretty helpful on difficult to replicate issues.
I was able to correct this by using the following change:
//if (customErrorsSection != null)
if (customErrorsSection != null && this.Context.Error != null)
This respects the typical error handling properly, as the context.Error will be null in cases where you explicitely raise the exception in Elmah, but is not null when falling through default error handling (not via a catch or if caught and re-thrown). This causes Ronnie's solution to respond similar to the .Net error handling logic.