MVC4 ExceptionContext ExceptionHandled Property no try catch block ELMAH - c#

I am trying to install and test ELMAH for the fist time.
I think I have setup everything correctly.
I know ELMAH is designed to log unhandled exceptions.
I use a standard template MVC 4 application generated by Visual Studio 2012
In a HomeControler class I throw an error without try and catch block
public ActionResult About()
{
// Throw a test error so that we can see that it is handled by Elmah
// To test go to the ~/elmah.axd page to see if the error is being logged correctly
throw new Exception("A test exception for ELMAH");
return View();
}
In my opinion this an unhandled exception.
Further I use a HandleErrorWithELMAHAttribute class to handle the error. This construct is shown in many ELMAH tutorials originally posted here:
How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
The code that bothers me is:
public override void OnException(ExceptionContext context)
{
base.OnException(context);
var e = context.Exception;
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}
In the if statement the property contect.ExceptionHanled is checked.
This property is set to true, so the thrown error is not logged.
Can you explain why it is set to true while there is no try-catch.
Best Regards,
Sebastian

Related

ASP.NET Core, handle expected exceptions

I'm working on a pretty simple CRUD controller in ASP.NET core and now I'm facing the issue, that I need to handle errors like working with an not existing entity (returning a 404) or creating a duplicate (returning a 400).
So now I'm wondering what is the most idiomatic way in ASP.NET Core to fetch expected errors and return the correct status code for them.
One way could be to throw exceptions in the services that are responsible for the actual CRUD and catch them inside of the controller:
/// Inside service...
public void Create(Entity entityDetails) {
if (entityAlreadyExists(entityDetails)) {
throw new EntityDuplicateException();
}
// ...
}
/// Inside controller...
[HttpPost("{operatorClientId}")]
public void CreateEntity(Entity entityDetails) {
try {
_entityService.CreateEntity(entityDetails);
return Ok();
} catch (EntityDuplicateException e) { // Some self defined exception type
return BadRequest(/* Some details about the entity */);
}
}
This seems a bit repetitive to me because a lot of code would be always the same, but it makes clear what to expect from the API at the first glance. I think this could also get a bit messy when more exceptions need to be handled.
Another approach could be to implement a method for UseExceptionHandler, where all exception types are handled like:
switch (exceptionHandlerPathFeature.Error)
{
case EntityDuplicateException e:
context.Response.StatusCode = 400;
// More details to response here
break;
case EntityNotFoundException e:
context.Response.StatusCode = 404;
// More details to response here
break;
case {} e:
context.Response.StatusCode = 500;
// More details to response here
break;
}
This would be a single place for the handling of expected (and unexpected) exceptions. But you cannot see at first glance what the controller will return. And it can get harder to obtain all required information for a meaningful error message in the response (in the controller every information is directly available, with the ExceptionHandler I need to wrap all information into the exception).
An exception, as its name applies, should only be used in exceptional circumstances, not for control flow. An entity not being found or already existing is not exceptional; it's a common case that you should be handling explicitly in your data-access code. On the other hand, a deadlock in your database is.
Instead of having your data-access methods throw exceptions, make them return something like a Tuple of (bool success, string errorMessage). In the controller, check the success member - if it's false, return the errorMessage to the caller.
As for your global exception handler, leave it generic. It should do little more than log all exceptions to a central location, so that you can inspect those logs regularly to determine whether your app really is encountering exceptional circumstances on a regular basis - in which case you need to fix it.

Is it possible to change the value returned by Server.GetLastError()?

I have written an HttpModule for ASP.NET which will inspect the current exception (if any) and wrap it in a new exception with a unique identifier. Then I want Elmah to log that wrapping exception.
My module is working and is sitting in front of the Elmah module, however I can't work out how to change the Server.GetLastError() so that my new exception will be logged!
I have tried:
var originalException = context.Server.GetLastError();
var app = (HttpApplication)sender;
var context = app.Context;
context.ClearError();
context.AddError(new WrapperException(originalException));
But doing so makes the context.Error property return null.
In fact ClearError doesn't do what it's advertised to do: "Clears all errors for the current HTTP request."
It doesn't do this. The AllErrors array still contains exceptions; all it does make GetLastError return null.
You cannot set context.AllErrors, nor poke something into the array (it's a copy).
You also cannot throw a new exception: the error page only sees the original exception, and Elmah doesn't even log it.
I'm beginning to think that it's not possible.
If the only reason you want to replace Server.GetLastError() is to make sure that ELMAH logs the right exceptions, there may be a better approach. ELMAH's filtering feature can be used to override the logged exception. To do so, add the following code to your Global.asax.cs file:
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs args)
{
var httpContext = args.Context as HttpContext;
ErrorLog.GetDefault(httpContext).Log(new Error(new WrapperException(args.Exception)));
args.Dismiss();
}
The ErrorLog_Filtering method is called by ELMAH just before logging any uncaught exceptions to the configured error log. In the example, I pull the information about the error happening (args.Context and args.Exception) and wrap the thrown exception in a new exception (WrapperException). By logging the new exception using the ErrorLog.GetDefault(...).Log(...) method, I make sure that ELMAH doesn't call the ErrorLog_Filtering method recursively (it would if you used ErrorSignal...Raise()). Finally, I dismiss the original exception, to avoid the error being logged twice.

catch exceptions from all Pages

I am trying to log all "general errors" from the site I am maintaining, to debug the code-behind, without having to show the detailed error page to the users. The web.config says:
<customErrors mode="RemoteOnly" defaultRedirect="~/GeneralError.html"/>
which works for problems like divide by zero, null-reference exceptions, missing dll, etc. After lots of reading here I found that these are the "unhandled exceptions", but do not reach the handler Application_Error() in global.asax. What does work, on a per-page basis, is:
protected override void OnError(EventArgs e)
{
Exception TheException = Server.GetLastError();
// todo: log the exception to the database
base.OnError(e); //pass the exception to the regular handler
return;
}
But could this be changed to log the unhandled exceptions of all pages with a single method? How can I overwrite, or modify, the base class Page?
I also thought of put logging in the code-behind of GeneralError.aspx, but then there would be no stack trace or exception message, and also any user may call this page directly just to annoy us.
I investigated using some IErrorHandler interface mentioned in some posts, but this should relate to WinForm apps, not to asp.net web apps.
Use the Application_Error method in your Global.asax file.
Inside your Application_Error method implementation call Server.GetLastError(), log the details of the exception returned by Server.GetLastError().
Exception LastError;
String ErrMessage;
LastError = Server.GetLastError();
if (LastError != null)
ErrMessage = LastError.Message;
else
ErrMessage = "No Errors";
Response.Write("Last Error = " + ErrMessage);
See here at MSDN or if you want a complete Microsoft Example at Complete Example for Error Handlers.
Create a new class:
public class MyPage : System.Web.UI.Page
{
protected override void OnError(EventArgs e)
{
Exception TheException = Server.GetLastError();
// todo: log the exception to the database
base.OnError(e); //pass the exception to the regular handler
return;
}
}
and instead of inheriting from System.Web.UI.Page inherit from MyPage on your individual pages.
I am currently studying https://msdn.microsoft.com/en-us/library/aa479332.aspx, found through https://stackoverflow.com/a/138088/1845672 which talks about exactly my problem with the exact same approach: provide a solution to log errors on all pages in a maintainable way without modifying all pages.
The download link on the microsoft site is stale. According to: https://code.google.com/p/elmah/issues/detail?id=64 the sources are moved from the microsoft site to: elmah.googlecode.com/files/GDN-ELMAH-1.0.5527-setup.zip .
Looks like this is a complete solution . . .

Exceptions and testing - how to unit test something for specific exception when I catch them all?

I'm writing MVC4 web application. Generally I try to put "try{}catch{}" block inside every controller method that returns ActionResult to the user. I do it in order to catch all Exceptions and display appropriate message, so user will never see something like:
"Reference not set to an instance of an object"
My controllers usually looks like this:
try
{
}
catch(MyFirstCustomException ex)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
catch(MySecondCustomException ex) (and so on...)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
catch(Exception ex)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
However now I got the following situation: I have AccountController and a LogIn method, I want to write a unit test (using Microsoft Unit Testing Framework), that will assert that user which haven't activated his account, won't be able to log in. I have a special Exception named UserNotActivatedException that is thrown, when such attempt is detected. Problem is - since I catch all my exceptions within a controller, my test will never actually see this exception itself - thus the test will always fail. I managed to bypass the problem by creating special status enum for my model which looks like this:
public enum LoginViewModelStatus
{
NotLoggedIn = 0,
LoginSuccessfull = 1,
LoginFailed = 2,
UserNotActivatedException = 3,
UnknownErrorException = 100
}
and by setting it to a certain value when something is happening (so when I catch my special UserNotActivatedException - I set loginModelStatus to UserNotActivatedException and so on)
My questions:
Are there any nicer alternatives to this?
I'm thinking of using this design in other controllers as well, are there any downfalls here?
Is it good design to use a lot of custom exceptions for displaying messages for users, or would it be better to use more mini if(someCondition){return false;} tests?
You could wrap the code inside the try part in order to be able to unit test this part.
Here, the unit testable part is simply "wrapped" inside the MyUnitTestableMethod method :
try
{
MyUnitTestableMethod();
}
catch(MyFirstCustomException ex)
{
// ...
}
catch(MySecondCustomException ex) (and so on...)
{
// ...
}
catch(Exception ex)
{
// ...
}
KISS : Keep It Sanely Simple (or Keep It Simple and Stupid) :)
You should test that code returns expected results in all cases and more or less ignore how method does its work.
I.e. in your case Controller converts multiple exceptions into different view - test that when you feed data that causes exception scenario the Controller returns view you expect.
If lower levels of methods used by controller may throw exception - test them too, but this time for throwing particular exceptions.
It is up to you how many exceptions is enough. Good logging of exceptions is probably more important than variety. In most cases you should not show information from exception to a user anyway, but rather something like "Catastrophic error. If need assistance the error was logged with id AB455". All "expected exception" cases should be handled and presented to user as normal flow.
Note that it is ok to throw exceptions from actions as long as you have code that handles all exceptions. Action filter like HandleErrorAttribute can be used to configure exception policy for particular action/whole application.
It seems as you have your code "too stable". That is, your logic can never generate errors. It is good from a stability point of view but not very testable.
I would in this case have a class to handle the custom logic catch all exceptions generated from that class before returning ActionResult to separate the logic.
class ActionClass
{
public bool HandleLogin(...)
{
...
}
}
and use the class like this:
try
{
ActionClass action = new ActionClass();
action.HandleLogin(...)
}
// Catchblock here
This will allow you to test the logic.

What is the equivalent to SoapExtension for JSON WebMethods?

I've a few web methods that I use to call some external services like the Google Calendar API, obviously these can be extremely brittle.
Unfortunately I now realise that any error thrown on these methods are not causing an exception to bubble up to Global.asax which is where errors are getting logged in this application.
I have seen suggestions to wrap the method in a try/catch, which is a stupid way of doing it as there are a variety of errors that ASP.Net will silently swallow still.
In trying to find a solution I've seen a lot of references to SoapExtension, which is exactly what I want to do but doesn't get fired as I'm returning Json. What I really want is a way to catch the error just like that.
Any pointers appreciated, I still can't understand how the ASP.Net team could have thought that silently swallowing errors like this was a bright idea.
So for example a method like this:
[WebMethod]
[ExceptionHandling] //can I write a handler like this to catch exceptions from JSON webservices?
static public void DeleteItem(string id)
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
There is no equivalent to SoapExtension for JSON WebMethods and having custom errors turned on in your production site will result in a generic error message being returned to the client, no error is ever raised on the server. You cannot circumvent this.
If you inspect the code using something like ILSpy, there is no way to pass a method or class to page WebMethods like SoapExtension. The error is swallowed by ASP.Net as it invokes the web method, the only notification you will get is a HTTP 500 error sent to the client with a total generic error message.
In 4.0, WebMethods get called by this:
// System.Web.Script.Services.RestHandler
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)
{
try
{
//snip irrelevant code
RestHandler.InvokeMethod(context, methodData, rawParams);
}
catch (Exception ex)
{
RestHandler.WriteExceptionJsonString(context, ex);
}
}
So if invoking your method throws an error it will call the following code with a statusCode of 500, there's no re-throw in there and nothing else you can pass in called so unless I'm being blind it just gets swallowed silently. Even worse if you've got custom errors turned on, which any sane person will, it'll completely obfuscate the original cause:
// System.Web.Script.Services.RestHandler
internal static void WriteExceptionJsonString(HttpContext context, Exception ex, int statusCode)
{
//snip code setting up response
context.Response.TrySkipIisCustomErrors = true;
using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false)))
{
if (ex is TargetInvocationException)
{
ex = ex.InnerException;
}
if (context.IsCustomErrorEnabled)
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(AtlasWeb.WebService_Error, string.Empty, string.Empty)));
}
else
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(ex.Message, ex.StackTrace, ex.GetType().FullName)));
}
streamWriter.Flush();
}
}
I can't see a way around it, looks like WebMethod is not ready for production code, shame.
It's not so much they get disappeared, it's more that they get passed out to the calling client. Since however you don't always want to (or should) reveal such intimate details of your service, you can prevent errors bubbling out of your service. This gives the impression of them disappearing.
Wrapping the inner detail in a try-catch is about the best way to cope with any errors. Within the method you're dealing with standard error trapping. So I think you'd want something like:
[WebMethod]
static public string DeleteItem(string id)
{
try
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
catch(Exception ex)
{
log.fatal(ex);
return "error";
}
}
If anything throws an exception within the try-catch it'll be caught. ASP.Net won't interfere with it, unless the methods you are calling have been specifically coded to do so.
Edit
If the GoogleCalendarAPI class is in turn calling a method, such as ExecuteWebServiceCall with catches the Exception, then you'd have to parse the response. I'd hope they gave you some other clue, like a response code, to indicate an error state. You could then wrap that in an Exception, throw it have it caught by your default error handler.

Categories