Custom Exception Filter not being hit in asp.net MVC - asp.net-mvc

I have a custom exception filter that I'm using to catch a custom exception that I wrote but for some reason when I throw my exception, it's not ever getting to the filter. Instead I just get an error that my exception was not handled by user code. Can anyone please provide some advice/assistance as to how I should have this set up? Relevant code is below:
// controller
[CustomExceptionFilter]
public class SomeController : Controller
{
public SomeController()
{
}
public ActionResult Index()
{
SomeClass.SomeStaticMethod();
return View();
}
}
that's the controller with the customexception attribute
// some class (where exception is being thrown)
public class SomeClass
{
public static void SomeStaticMethod()
{
throw new MyCustomException("Test");
}
}
that's the class (for my test) that throws the exception (I've also tried throwing it directly on the controller).
// Custom exception filter (want this to catch all unhandled exceptions)
public class CustomExceptionFilter : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() == typeof(MyCustomException))
{
// do stuff
}
}
}
that's the custom exception filter...it's never being reached when the code is executed and the exception is thrown. Instead I get the error mentioned above. Everything I've read indicates that this is the proper way to set this up, but when I put breakpoints in my custom filter, it's never being hit....
What am I missing here?
TIA

Once you've handled your error, you need to let the filter context know it has been handled. Like this:
filterContext.ExceptionHandled = true;
This should be in your '// do stuff' section.
I've copied your code, and the filter is getting called fine. The only difference I made was I added the exceptionHandled code and adding my breakpoint at that line.

Related

How to handle exceptions thrown in DataProvider methods centrally

When a DataProvider fetch or count method throws an exception, e.g. because the user is not authorized, how could I handle these exceptions centrally? I know there is HasErrorParameter interface to show error views when there is an exception thrown when routing. But these error views are not triggered when DataProvider throws the exception.
Example:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
...
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
throw new UnsupportedOperationException("test");
}
}
#Route("failed")
public class FailView extends VerticalLayout
implements HasErrorParameter<UnsupportedOperationException> {...}
Even if I do a try catch within the DataProvider methods, I don't see how I could navigate to the appropriate error view just by using the caught exception and not the view component class (this wouldn't trigger setErrorParameter method).
BTW: I miss the router exception handling topic in Vaadin Flow 13 documentation. I wonder why they removed it.
I believe all Exceptions that don't occur while routing will be given to the ErrorHandler of the VaadinSession the error occured in.
The best way to set the ErrorHandler seems to be to override the sessionInit method in a custom SessionInitListener
You can add a custom SessionInitListener inside the servletInitialized method of a custom VaadinServlet.
class CustomServlet extends VaadinServlet{
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(new CustomSessionInitListener());
}
}
And that SessionInitListener (in this example CustomSessionInitListener) has to set the errorHandler of the sessions that get initialized.
class CustomSessionInitListener implements SessionInitListener{
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().setErrorHandler(new CustomErrorHandler());
}
}
For further information on how to create your own Servlet take a look at Vaadin's tutorial page(you need to scroll down to "Customizing Vaadin Servlet")
Edit:
To show the error page you need to get Vaadin to reroute to an error. To achieve that we can use an BeforeEnterEvent, BeforeEnterEvents have a rerouteToError method which we can use to let Vaadin show our ErrorView.
But we also want to pass along the Exception instance, so we have to store that as well. I did exactly that with the following class:
#Route("error-view") // Route shown in the user's browser
public class ErrorViewShower extends Div implements BeforeEnterObserver {
// Class to store the current Exception of each UI in
private static class UIExceptionContainer extends HashMap<UI, Exception> {
}
// Method to call when we want to show an error
public static void showError(Exception exception) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Creating and setting the exceptionContainer in case it hasn't been set yet.
if (exceptionContainer == null) {
exceptionContainer = new UIExceptionContainer();
VaadinSession.getCurrent().setAttribute(UIExceptionContainer.class, exceptionContainer);
}
// Storing the exception for the beforeEnter method
exceptionContainer.put(UI.getCurrent(), exception);
// Now we navigate to an Instance of this class, to use the BeforeEnterEvent to reroute to the actual error view
UI.getCurrent().navigate(ErrorViewShower.class);// If this call doesn't work you might want to wrap into UI.access
}
#Override
public void beforeEnter(BeforeEnterEvent event) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Retrieving the previously stored exception. You might want to handle if this has been called without setting any Exception.
Exception exception = exceptionContainer.get(UI.getCurrent());
//Clearing out the now handled Exception
exceptionContainer.remove(UI.getCurrent());
// Using the BeforeEnterEvent to show the error
event.rerouteToError(exception, "Possible custom message for the ErrorHandler here");
}
}
Usage of it in combination with the error handler looks like this:
public class CustomErrorHandler implements ErrorHandler {
#Override
public void error(ErrorEvent event) {
// This can easily throw an exception itself, you need to add additional checking before casting.
// And it's possible that this method is called outside the context of an UI(when a dynamic resource throws an exception for example)
Exception exception = (Exception) event.getThrowable();
ErrorViewShower.showError(exception);
}
}
Edit2:
As it turns out that Exceptions occuring inside internal method calls don't get handled by the UI's ErrorHandler or the VaadinSession's ErrorHandler but instead by another error handler which causes the client side to terminate and show the Error Notification,
a solution is to catch the Exceptions inside the methods of the DataProvider and pass them to ErrorViewShower.showError() and still return without any Exception flying the stacktrace upwards. (Or don't throw any Exception yourself and instead simply pass a new to the ErrorViewShower.showError() method).
By returning normally Vaadin doesn't even know something went wrong.
ErrorViewShower.showError() calls ui.navigate, that navigation command seems to get "queued" behind the calls to the DataProvider, meaning the view of the user will change in the same request.
Dataprovider with such an implementation:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
try{
//Code that can throw an Exception here
}catch(Exception e){
ErrorViewShower.showError(e);
//We have to make sure that query.getLimit and query.getOffset gets called, otherwise Vaadin throws an Exception with the message "the data provider hasn't ever called getLimit() method on the provided query. It means that the the data provider breaks the contract and the returned stream contains unxpected data."
query.getLimit();
query.getOffset();
return Stream.of(); //Stream of empty Array to return without error
}
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
//Second way i mentioned, but this will not catch any Exception you didn't create, where as the try...catch has no way to let any Exception reach Vaadin.
if(badThingsHappened){
ErrorViewShower.showError(new UnsupportedOperationException("Bad things..."));
return 0;//Exiting without error
}
}
}

Exception logging in ASP.NET MVC Application

Currently I log exceptions as follows-
public ActionResult Login()
{
try
{
throw new Exception("test");
}
catch (Exception ex)
{
LogException(ex);
}
return View();
}
I can get a clear & detail information about the exception from LogException() method here. See below-
Created on: 27-Dec-2016, 06.34.33 PM
Type: System.Exception
Error Description: test
Source File: ...\Controllers\LoginController.cs
Method: Login
Line: 20
Column: 17
I tried the same method in -
public class MyCustomAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
LogException(ex)
}
}
public class MyCustomAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
LogException(ex)
}
}
I also tried overloading OnException method-
public abstract class BaseController : Controller
{
public BaseController() {}
protected override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
filterContext.ExceptionHandled = true;
LogException(ex);
}
}
In all three abose cases I am getting no information about Source File, Method, Line and Column-
Created on: 27-Dec-2016, 06.44.45 PM
Type: System.Exception
Error Description: test
Source File:
Method: <BeginInvokeAction>b__1e
Line: 0
Column: 0
This is my method to log exception-
public static void Create(Exception exception, String rootDirectoryPath)
{
try
{
StackTrace st = new StackTrace(exception, true);
StackFrame frame = st.GetFrame(st.FrameCount - 1);
string fileName = frame.GetFileName();
string methodName = frame.GetMethod().Name;
int line = frame.GetFileLineNumber();
int col = frame.GetFileColumnNumber();
//Other code .....
}
catch (Exception)
{
//do nothing.
}
}
My question is, is it possible to retrieve Source File, Method, Line and Column information from those three cases?
I would not like to handle exceptions by writing try .. catch.. every time.
I have heard about ELMAH & Log4Net. But not sure whether those library able to supply my desired information from exceptions.
Sorry for the long question.
I'm the author of Coderr which also takes care of tracking exceptions for you.
the problem with your solution is that the exception filter (and the ASP.NET framework calls to invoke it) will be part of the stack trace, thus getting frames from the stack trace object wont work.
Also note that if the .PDB file is not included when you put your web site in production you wont get file numbers.
The most common approach is just to log exception.ToString() which will include line numbers in the stack trace (if there is a PDB file).
Why do you want to use your custom approach?
You are reinventing the wheel as ELMAH will do the job just fine. Did you give it a try? Just a Nuget package and some configuration to be fully set.
You can read more about it from Scot Hanselman or read the tutorial.

How to log custom exception with additional Data property to Elmah?

I have a custom exception, where I have overriden the Data property using reflection like the following...
public class MyCustomException : Exception
{
private readonly SomeModel _log;
public MyCustomException(SomeModel log)
: base(string.Format("Could not insert to some table"))
{
_log = log;
}
public override System.Collections.IDictionary Data
{
get
{
var data = new Dictionary<string, object>();
foreach (PropertyInfo pinfo in _log.GetType().GetProperties())
{
data.Add(pinfo.Name, pinfo.GetType().GetProperty(pinfo.Name));
}
return data;
}
}
}
When the above exception is thrown, it gets logged to elmah but the Data is not logged.
What changes do I have to make so that the Data is also logged to elmah ? Please advice.
The Detail property of the Elmah.Error object - which is then processed by an ErrorLog class - is built from the ToString() method of the exception.
// Elmah.Error
public Error(Exception e, HttpContext context)
{
// snip
this._detail = e.ToString(); // here
// snip
Add your data to an override of the ToString method in the MyCustomException to see it in Elmah.
Your question is currently the issue with most stars on the ELMAH issue tracker:
https://code.google.com/p/elmah/issues/detail?id=162
#samy may be more correct, but I have also found another possible option that works for my situation. I am using elmah in a webapi2 project where users are anonymous and in one particular controller I want to record some context of the request from the viewmodel (in my case, an email address but I could potentially record more data) and I want to be able to associate errors to email so I can determine, after an error, if the same user was able to submit an order successfully.
In my controller, I perform a number of database calls in a transaction and then submit an order to paypal, all within a try/catch block. In the catch, I create a new exception instance with a message containing the email and set the innerException property to the thrown exception and throw the new exception.
I know it is possible to lose some stack trace information, I tested this in my context and the stack trace seems to be maintained but exceptions occur inside the controller because there are not many layers to this particular controller and application. If anyone has a similar situation, this method might be the quickest and easiest.
catch (Exception ex)
{
Exception newException = new Exception(viewModel.ContactEmail, ex);
throw newException;
}
This assumes you have a exception filter, such as below (for webapi), and the filter is registered as global in global.asax.
public class LogExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (HttpContext.Current != null)
{
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
}

Trouble with HandleError

I have the following Action Method:
[HandleFtmsError]
public ActionResult PerformanceChart(ChartViewModel chart)
{
var x = 1;
var y = 0;
var z = x/y;
return Json("");
}
where HaneleFtmsError is defined as:
public class HandleFtmsErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (context.ExceptionHandled)
RaiseErrorSignal(context.Exception);
}
private static void RaiseErrorSignal(Exception e)
{
var context = HttpContext.Current;
ErrorSignal.FromContext(context).Raise(e, context);
}
}
I thought that attribute over the action method would have been executed with a DivideByZero exception, but it's not working. All I'm seeing is the code breaks on the line where I'm doing the division. Am I doing something wrong?
When you say "the code breaks" do you mean it's breaking into the debugger? That's probably just the standard debugger behaviour, which you can change via the Debug menu's "Exceptions..." item. If you hit F5 again - or run without debugging - you may see the behaviour you expect.
MVC isn't preventing the exception from being thrown (which is what the debugger's looking for) - it's just handling the exception by noticing the attribute on the controller and passing the information on appropriately. At the point where the debugger's breaking in, there hasn't been a chance for it to do that yet.

ASP.Net MVC Error handling using Action Filters Attributes

I am trying to implement Error handling using Action Filters Attributes as per ScottGu's blog
My code is as follows:
[HandleError]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "CustomError")]
public class ArticlesController : Controller
{
public object OhDearACrash()
{
throw new Exception("Oh Dear");
}
public object NullRefCrash()
{
throw new NullReferenceException();
}
I am encountering an issue where I am never able to hit the CustomError view as I receive an exception when the exception is thrown
OhDearACrash: Exception was unhandled by user code
NullRefCrash: NullReferenceException was unhandled by user code
and so the unhandled exception is picked up by the Default [HandleError] which routes to View/Shared/Error.aspx which handles the error.
How do I handle the unhandled exception?
The action filters are executed one by one. In your case, the problem is probably that the generic HandleError action filter is executed before the specific one.
You can influence the order of execution by setting the 'Order' property of your action filter:
[HandleError(Order = 2)]
[HandleError(Order = 1, ExceptionType = typeof(NullReferenceException), View = "CustomError")]
public class ArticlesController : Controller
{
}

Resources