How to log custom exception with additional Data property to Elmah? - asp.net-mvc

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);
}
}
}

Related

Return to original view from MVC action filter

Im working on a asp.net core website and im trying to make som global validation exception handling using Filters. The backend can at random places throw fluentapi ValidationException and I want to catch these and show the error messages to the user. This filter only cares about ValidationExceptions. All other exceptions will be handled later..
Instead of using a try/catch in every post action in all my controllers, I want to use a filter that catches only ValidationExceptions, add the errors to the ModelState and then return to the original view with the updated ModelState.
I have tried many things but every time I just get a blank page after the filter finishes. I can easily set a new RedirectToRouteResult witht the controller and action from the context. But then I dont have the ModelState and values the user entered..
public class PostExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is FluentValidation.ValidationException)
{
var ex = context.Exception as FluentValidation.ValidationException;
context.Exception = null;
context.HttpContext.Response.StatusCode = 200;
context.ExceptionHandled = true;
foreach (var item in ex.Errors.ToList())
{
context.ModelState.AddModelError(item.PropertyName, item.ErrorMessage);
}
// Done with the stuff I want.
// Now please go back to the original view with the updated modelstate and values
}
else if (context.Exception is UnauthorizedAccessException)
{
// Do something else...
}
else
{
// Do something else...
}
base.OnException(context);
}
}
You cannot access the particlar Model(related to Action Method) in Exception Filters. So you have to handle the error at Controller level if you want to add Errors to model.
try
{
//Do something
}
Catch(Exception e)
{
ModelState.AddModelError(string key, string errorMessage);
Return View(model)
}
The error message will present itself in the <%: Html.ValidationSummary() %> in your View
Without try-catch blocks you won't know if exception occured in Action Method, So that you can add Custom Errors to Model.

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
}
}
}

How to handle errors in my CustomAutorize attribute in asp.net 3.0 Application

I am working on an asp.net MVC 3.0 Application. I am using using my own CustomRoleProvider
and CustomErrorHandler by overriding default attributes.
Every thing is working fine. But ,the problem is with the exception handling.
While testing the application , tester has given invalid DB connection to test.
The result is , Custom Error Handler is not rendering Error View , instead it is routing the original path
For ex:
I am running my application as
Home/Index
It is first hitting Custom Role Provider to fetch the roles for the application
Since , the Db Connection is not correct , it is raising exception that "Not able to Connect"
Now , Instead of routing to Error View along with this error message. It is routing to Home Controller and Index action.
**The code for my Custom Error Handler is as Follows**
public class CustomHandleErrorAttribute : HandleErrorAttribute // Error handler
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
{
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
{
return;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = AjaxError(filterContext.Exception.Message, filterContext);
}
else
{
filterContext.ExceptionHandled = true;
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult
{
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
}
}
protected JsonResult AjaxError(string message, ExceptionContext filterContext)
{
if (String.IsNullOrEmpty(message))
message = "Something went wrong while processing your request. Please refresh the page and try again.";
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
return new JsonResult { Data = new { ErrorMessage = message }, ContentEncoding = System.Text.Encoding.UTF8, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
In the above code , after setting up filterContext.Result . It is not rendering Error View as Expected.
Please correct/suggest me, where i am going wrong..
Updated:
public class CustomRoleProvider : RoleProvider // Custom role provider
{
public override string[] GetRolesForUser(string username)
{
// Fetching roles for user from database
}
// Some other Methods
}
This is method is generating exception , since it is trying to connect to wrong connection
Updated2:
1) I am using Custom Error Handler for the entire controller.
2) I need to catch all the exceptions including Ajax Errors
3) I have included my code for Custom Error Handler Above
4) I am also using CustomRole Provider for entire controller
5) Here, I am trying to generate exception , by giving wrong database connection
6) I am running the URL : Home/Index
7) Before going to thatr URL, it is hitting the methods in Role Provider class since i am using it as a attribute
8) Since, i have gave wrong DB Connection , It is generating exception
9) Then, it fires on exception method of Custom error handler
10) Building the Error Model for the error view
11) But, here is the problem. Instead of rendering Error View , it is going to index method of the Home Controller.
12) But, i need Error View to be rendered here, because it has failed to connect to database and getting roles . I want furthuer execution of URL Home/Index to be stopped here.
Hope this clarifies the problem..i am running in to. please feel free to ask me for furthuer details/Clarification
HandleError is designed to be able to register multiple filters (for example for different exceptions). One filter can handle only some specific exceptions or error cases and another unhandle cases can be handled by another HandleError. I suppose that currently both standard and your [CustomHandleError] filter are applied. You can set the Order property to an integer value that specifies a priority from -1 (highest priority) to any positive integer value. The greater the integer value is, the lower the priority of the filter is. You can use Order parameter for example (see here) to make your filter working before. More full description of the order you can find in the MSDN documentation.
The answer, this one and the article for example provide small examples of usage Order property of HandleError.

ELMAH - Using custom error pages to collecting user feedback

I'm looking at using ELMAH for the first time but have a requirement that needs to be met that I'm not sure how to go about achieving...
Basically, I am going to configure ELMAH to work under asp.net MVC and get it to log errors to the database when they occur. On top of this I be using customErrors to direct the user to a friendly message page when an error occurs. Fairly standard stuff...
The requirement is that on this custom error page I have a form which enables to user to provide extra information if they wish. Now the problem arises due to the fact that at this point the error is already logged and I need to associate the loged error with the users feedback.
Normally, if I was using my own custom implementation, after I log the error I would pass through the ID of the error to the custom error page so that an association can be made. But because of the way that ELMAH works, I don't think the same is quite possible.
Hence I was wondering how people thought that one might go about doing this....
Cheers
UPDATE:
My solution to the problem is as follows:
public class UserCurrentConextUsingWebContext : IUserCurrentConext
{
private const string _StoredExceptionName = "System.StoredException.";
private const string _StoredExceptionIdName = "System.StoredExceptionId.";
public virtual string UniqueAddress
{
get { return HttpContext.Current.Request.UserHostAddress; }
}
public Exception StoredException
{
get { return HttpContext.Current.Application[_StoredExceptionName + this.UniqueAddress] as Exception; }
set { HttpContext.Current.Application[_StoredExceptionName + this.UniqueAddress] = value; }
}
public string StoredExceptionId
{
get { return HttpContext.Current.Application[_StoredExceptionIdName + this.UniqueAddress] as string; }
set { HttpContext.Current.Application[_StoredExceptionIdName + this.UniqueAddress] = value; }
}
}
Then when the error occurs, I have something like this in my Global.asax:
public void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
var item = new UserCurrentConextUsingWebContext();
item.StoredException = args.Entry.Error.Exception;
item.StoredExceptionId = args.Entry.Id;
}
Then where ever you are later you can pull out the details by
var item = new UserCurrentConextUsingWebContext();
var error = item.StoredException;
var errorId = item.StoredExceptionId;
item.StoredException = null;
item.StoredExceptionId = null;
Note this isn't 100% perfect as its possible for the same IP to have multiple requests to have errors at the same time. But the likely hood of that happening is remote. And this solution is independent of the session, which in our case is important, also some errors can cause sessions to be terminated, etc. Hence why this approach has worked nicely for us.
The ErrorLogModule in ELMAH (version 1.1 as of this writing) provides a Logged event that you can handle in Global.asax and which you can use to communicate details, say via HttpContext.Items collection, to your custom error page. If you registered the ErrorLogModule under the name ErrorLog in web.config then your event handler in Global.asax will look like this:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
var id = args.Entry.Id
// ...
}

Custom Exception Filter not being hit in 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.

Resources