Good error handling practice - asp.net

What is a good error handling practice for an asp.net site? Examples? Thanks!

As with any .net project I find the best way is to only catch specific error types if they are may to happen on the given page.
For example you could catch Format Exceptions for a users given input (just incase JavaScript validation fails and you have not use tryparse) but always leave the catching of the top level Exception to the global error handler.
try
{
//Code that could error here
}
catch (FormatException ex)
{
//Code to tell user of their error
//all other errors will be handled
//by the global error handler
}
You can use the open source elmah (Error Logging Modules and Handlers) for ASP.Net to do this top level/global error catching for you if you want.
Using elmah it can create a log of errors that is viewable though a simple to configure web interface. You can also filter different types of errors and have custom error pages of your own for different error types.

One practice that I find to be especially useful is to create a generic error page, and then set your defaultRedirect on the customErrors node of the web.config to that error page.
Then setup your global.asax for logging all unhandled exceptions and then put them (the unhandled exceptions) in a static property on some class (I have a class called ErrorUtil with a static LastError property). Your error page can then look at this property to determine what to display to the user.
More details here: http://www.codeproject.com/KB/aspnet/JcGlobalErrorHandling.aspx

Well, that's pretty wide open, which is completely cool. I'll refer you to a word .doc you can download from Dot Net Spider, which is actually the basis for my small company's code standard. The standard includes some very useful error handling tips.
One such example for exceptions (I don't recall if this is original to the document or if we added it to the doc):
Never do a “catch exception and do nothing.” If you hide an exception, you will never know if the exception happened. You should always try to avoid exceptions by checking all the error conditions programmatically.
Example of what not to do:
try
{
...
}
catch{}
Very naughty unless you have a good reason for it.

You should make sure that you can catch most of the errors that are generated by your application and display a friendly message to the users. But of course you cannot catch all the errors for that you can use web.config and defaultRedirect by another user. Another very handy tool to log the errors is ELMAH. ELMAH will log all the errors generated by your application and show it to you in a very readable way. Plugging ELMAH in your application is as simple as adding few lines of code in web.config file and attaching the assembly. You should definitely give ELMAH a try it will literally save you hours and hours of pain.
http://code.google.com/p/elmah/

Code defensively within each page for exceptions that you expect could happen and deal with them appropriately, so not to disrupt the user every time an exception occurs.
Log all exceptions, with a reference.
Provide a generic error page, for any unhandled exceptions, which provides a reference to use for support (support can identify details from logs). Don't display the actual exception, as most users will not understand it but is a potential security risk as it exposes information about your system (potentially passwords etc).
Don't catch all exceptions and do nothing with them (as in the above answer). There is almost never a good reason to do this, occasionally you may want to catch a specific exception and not do any deliberately but this should be used wisely.

It is not always a good idea to redirect the user to a standard error page. If a user is working on a form, they may not want to be redirected away from the form they are working on. I put all code that could cause an exception inside a try/catch block, and inside the catch block I spit out an alert message alerting the user that an error has occurred as well as log the exception in a database including form input, query string, etc. I am developing an internal site, however, so most users just call me if they are having a problem. For a public site, you may wish to use something like elmah.

public string BookLesson(Customer_Info oCustomerInfo, CustLessonBook_Info oCustLessonBookInfo)
{
string authenticationID = string.Empty;
int customerID = 0;
string message = string.Empty;
DA_Customer oDACustomer = new DA_Customer();
using (TransactionScope scope = new TransactionScope())
{
if (oDACustomer.ValidateCustomerLoginName(oCustomerInfo.CustId, oCustomerInfo.CustLoginName) == "Y")
{
// if a new student
if (oCustomerInfo.CustId == 0)
{
oCustomerInfo.CustPassword = General.GeneratePassword(6, 8);
oCustomerInfo.CustPassword = new DA_InternalUser().GetPassword(oCustomerInfo.CustPassword, false);
authenticationID = oDACustomer.Register(oCustomerInfo, ref customerID);
oCustLessonBookInfo.CustId = customerID;
}
else // if existing student
{
oCustomerInfo.UpdatedByCustomer = "Y";
authenticationID = oDACustomer.CustomerUpdateProfile(oCustomerInfo);
}
message = authenticationID;
// insert lesson booking details
new DA_Lesson().BookLesson(oCustLessonBookInfo);
}
else
{
message = "login exists";
}
scope.Complete();
return message;
}
}

Related

best Approach for Catching exception from my model class

I got lost on what/where/when i should catch exceptions inside my asp.net mvc web application. and how to display error messages for end users. For example i got the following Action method:-
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ServerToEdit serverToEdit)
{
try
{
if (ModelState.IsValid)
{
if (!(repository.IsIPUnique(serverToEdit.TechnologyIP.IPAddress, 0)))
{
ModelState.AddModelError("TechnologyIP.IPAddress", "Error occured. The Same IP might already assinged.");
return View(serverToEdit);
}
if (!(repository.IsServerNameUnique(serverToEdit.Server.Name, 0)))
{
ModelState.AddModelError("Server.Name", "Error occured. The Same Server Name might already assinged.");
return View(serverToEdit);
}
repository.InsertOrUpdateServer(serverToEdit.Server,serverToEdit.TechnologyIP,User.Identity.Name);
repository.Save();
return RedirectToAction("Index");
}
else
{
return View(serverToEdit);
}
}
catch (DbUpdateException)
{
ModelState.AddModelError(string.Empty, "Error occured. The Same IP/Name might already assinged.");
}
catch (DbEntityValidationException)
{
ModelState.AddModelError(string.Empty, "Error occured. User might not be defiend inside Active Directory.");
}
return View(serverToEdit);
}
I have the following questions regarding my code:-
i have two services methods (IsIPUnique & IsServeNameUnique); but is the Controller action method that best place to call these service methods from my repository model class?
currently if the service method fail, i am manually writing the model state error and return the view to the user. so is this the right way to write model state errors for the service methods?
if the DBUpdateException was raised, i am assuming that either the IP or serve-name already exists in the database (This might happen if the service methods fail to check in a high traffic application). but there are many other reasons for the DBUpdateExceptio to be raised. so my question is about what is the best approach to handle the DBUpdateException . and show exactly the reason it was raised for?
The controller is usually the right place to do your model state validation, but, as I discovered, if you're doing a lot of validation then it can easily bloat up your controller.
How I deal with validation heavy controller methods is to export the validation to a service, say a static method called ValidateServer(serverToEdit).
Then inside this method, run all your various checks, and build up a Dictionary called errors, and add an error to this dictionary if the check fails.
Then you can pass this dictionary back to your controller, and add the errors to ModelState using a foreach. This then leaves you with just your exception handling.
For serving the user info on your DBUpdateException, you should be able to get information on why it occurred via the exceptions .InnerException property, which you could pass to the user as your error message. So you would write:
catch (DbEntityValidationException ex)
{
ModelState.AddModelError(string.Empty, "Error occurred:" + ex.InnerException);
}
Or something along those lines!
Don't mix validation with exception handling.
It makes more sense to handle the exception in one centralized place and log and display errors to the end users. That way, you make your code much more readable and maintainable. CodeProject has a great article on this subject.
I think you can add another tier that wraps the calls and get back the result with some state and the error if any, and use modelstate in case of failure with the returned message, in this way your controller would be more readable.

ASP.NET Exception Handling presentation to user

I have added try catches in my site which work great. The next part in development is to set up some exception handling output. Obviously I don't want the site to crumble when an error occurs and I don't want a horrible error screen. So, somewhere in the middle would be nice.
I was thinking to have an exception handler user control in the main master page which wil output a nice little message when something goes wrong.
What would be the best way to pass the exception to this user control? I was thinking of using a session variable.
Does anyone have a solution to this problem?
Thanks
This is easier than you're making it. Create a page called error.aspx (or whatever you want to call it), and turn the CustomErrors on in the web.config.
<configuration>
<system.web>
<customErrors defaultRedirect="errors.aspx"
mode="On">
</customErrors>
</system.web>
</configuration>
There is a lot of configurability to handle different http errors or random exceptions. If you want, you can dump the exception message (or the exception itself) into the session or use the HttpServerUtility.GetLastError (documentation)and then output any number of different messages on the error page. This will even catch exceptions that you DON'T catch.
CustomErrors Documentation
Edit:
#Icarus indicates that OP is looking for a solution by which the page continues to render and output while only showing an error message within the page, so I'll provide an answer to that as well.
There are a few ways I've handled this in the past. For very complex pages where the behavior requested is fatal (the page result can't be known), I've always deferred to an error page as I mentioned above that includes major master elements. So the menu, header, etc are all still there, but the notification of cataclysm is evident.
The behavior you seem to be looking for is something I would use less out of a need to report exceptions than to report general messages. My preference is to have a user control that has a public bindable property that a message can be injected into. Such as (forgive the VB, I can't seem to find my C# version):
<BrowsableAttribute(True), Bindable(False), Category("Misc"), _
Description("Gets or sets the NavigateUrl property for the link to start a new request.")> _
Public Property MyImportantMessage() As String
Get
Return _myImportantMessage
End Get
Set(ByVal value As String)
_myImportantMessage= value
End Set
End Property
Then the output could be any string message you like or you could program in a number of presets, fetch from resources, whatever. As to how to get it in there, we have taken the MasterPage route. We have a method in our master pages which assigns such a property in the user control directly. In order for this method to be callable, each page that uses the master page has to include the VirtualPath property, otherwise the method will not be available at compile time.
<%# MasterType
VirtualPath="~/MasterPage.master" %>
This then allows the Master property to be called from within the page itself to call this property:
'Keeping the VB theme
Me.Master.SetImportantMessage(message)
Keep in mind that Me.Master from within the page is not the same as Page.Master. So your exception will either need to bubble up to the actual page or the page reference itself will have to be sent down to your usercontrols as a property.
If you're wanting to provide an error for a handled exception, it's really as simple as populating a label with some sort of a notice that the application had a problem processing a request. You would do this within the "catch" part of the statement. I've even gone as far as presenting different messages depending on the role of the user. For instance if an admin generates an error you could actually output the actual exception message, but if someone in a lesser role generates it it would show them a more generic error and send the admin group a message/email.
try
{
//your code here
}
catch (Exception ex)
{
if (Role!="Admin")
{
Label1.Text = "There was an error, the administrator has been notified."
}
else
{
Label1.Text = "Error: " + ex.Message + ": " + ex.StackTrace; //you could add further here if you want to look for an inner exception.
}
}

ASP.NET MVC 2: Avoiding loop when accessing 404 action directly

Like many people using ASP.NET MVC, I've implemented my own custom 404 error handling scheme using an approach similar to the one described here: How can I properly handle 404 in ASP.NET MVC?
(I actually discovered that post after implementing my own solution, but what I came up with is virtually identical.)
However, I ran into one issue I'm not sure how to properly handle. Here's what my 404 action in my ErrorController class looks like:
public ActionResult NotFound(string url)
{
url = (url ?? "");
if (Request.Url.OriginalString.Contains(url) &&
Request.Url.OriginalString != url)
{
url = Request.Url.OriginalString;
}
url = new Uri(url).AbsolutePath;
// Check URL to prevent 'retry loop'
if (url != "/Error/NotFound")
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
// Log 404 error just in case something important really is
// missing from the web site...
Elmah.ErrorSignal.FromCurrentContext().Raise(
new HttpException(Response.StatusCode,
String.Format("Resource not found: {0}", url)));
}
return View();
}
The part that's different from the answer in the other StackOverflow question I referenced above is how the 'retry loop' is prevented. In other other answer, the code that prevents the retry loop simply sets properties on a ViewModel, which doesn't seem to actually prevent the loop. Since the action is sending back a response code of 404, then accessing the action directly (by typing "/Error/NotFound" in the browser) causes an infinite loop.
So here's my question: Did I miss another, more obvious way to handle the retry loop issue, or is my approach a decent way to do this?
you can handel erros by enabling the customErrors mode in the web.config file and set it to redirect errors to your controller when any errors occurs.
see an example here

How to handle Object Data Source Exception handling in DotNetNuke

I've created several DNN (5.2.3) modules and I'm finding that if an error happens on the object data source (ODS) DNN will then show that error to everyone. A sample (though not a ODS specific error in this case) is shown below. This has no meaning to user. The exception should really be "Thing not found" or something like that to the user. The problem is that this DropDownList is bound to an ODS. So my questions are:
How in DNN can I override this behavior to show a helpful message using ODS binding?
How in DNN can I override this behavior to show a generalized exception for all errors for the current module, if a specific exception cannot be caught to give a helpful message to the user?
Sample:
'SelectedThingDropDown' has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value
PS> I know about try/catch Exceptions.ProcessModuleException(e,ex). The problem is this does not work for ODS binding, unless I'm doing something wrong.
There are number of places you can inspect while dealing with ODS and DropDownList. Here is list of some of them.
ODS_Selected event, check e.Exception: This object is having errors when calling Select method provided. It's null if there is no error. If you find error, you can disable the ddl and place a user friendly message in label for notification.
DDL_DataBound event: Don't directly bind the selected value, try to find the the dll items by value like :
ListItem item = ddl.Items.FindByValue('');
if(item != null) item.selected = true;
Keep in mind that module load exception will be there only if you are not handling the exceptions, if you feel they are not helpfull to user, you can override them by your own user notification method with the help of try catch as you said. but don't forget to create entries in the event log for the error so that you can track your errors and optimize them.
Good luck.

Hiding the stacktrace for an exception returned by a asp.net WebMethod?

I am using methods with the Attribute [WebMethod] in my aspx pages. I don't use any asp.net ajax but jQuery to call these methods and return objects in JSON. This all works fine.
Next I added an authorization check inside the webMethod, if the current user doesn't have access to the feature I need to let the calling JavaScript know.
So I am throwing an AccessViolationException exception which can then be parsed by the OnError callback function in JavaScript. This works too but the exception includes the full StackTrace and I don't want to make this available to the calling client.
What other ways I could use to return an "Access Denied" to the client when the WebMethod returns a business object?
I'm using ASP.Net 3.5SP1 and jQuery 1.32
You can also add a:
customErrors mode="On"/
in your web.config, this will cut away the stack trace and leave you only the exception message
Why propagate errors through the wire? why not use an error response ?
Just wrap your object in a response object wich can contain an error code for status and an error message to present to users.
As suggested by NunFur I changed my approach and rather than throwing an error, I return a 'richer' object.
There are at least two options, the first one would be to encapsulate my business object into a response object with some status properties. I tried this but it makes the JSON more complicated.
So rather than adding a new object I added two properties to my business object, something like ServiceStatus and ServiceMessage. By default these are 200 and '', but can be set by the WebMethod code if anything goes wrong (no access, proper error). In this case they business object will be 'empty' (no data). The JavaScript code then first checks for the ServiceStatus and reacts appropriately.
I add the two fields to all my objects that are returned by WebMethods, even a simple string. They have to implement an Interface with those two properties.
Now I have complete control over that goes over the wire in case something unexpected is happening.
Thanks for the input
I save exceptions for when things go really wrong. (e.g. can't connect to the database)
Either return nothing (null/nill/whatever), or return a false bool value.
Sorry that I don't have a better answer than that...I'll have to keep looking myself.
You could look at SoapException: http://msdn.microsoft.com/en-us/library/system.web.services.protocols.soapexception(VS.71).aspx
I'm just not sure, if it will work when it is called from JavaScript. Espeially if it's called with a get-request.
BTW AccessViolationException is to my best knowlegde ment to be thrown when the application is accessing memory it has no access to.
/Asger

Resources