Scenarios for Throwing Exceptions
- by Joe Mayo
I recently came across a situation where someone had an opinion that differed from mine of when an exception should be thrown. This particular case was an issue opened on LINQ to Twitter for an Exception on EndSession. The premise of the issue was that the poster didn’t feel an exception should be raised, regardless of authentication status. As first, this sounded like a valid point. However, I went back to review my code and decided not to make any changes. Here's my rationale: 1. The exception doesn’t occur if the user is authenticated when EndAccountSession is called. 2. The exception does occur if the user is not authenticated when EndAccountSession is called. 3. The exception represents the fact that EndAccountSession is not able to fulfill its intended purpose - to end the session. If a session never existed, then it would not be possible to perform the requested action. Therefore, an exception is appropriate. To help illustrate how to handle this situation, I've modified the following code in Program.cs in the LinqToTwitterDemo project to illustrate the situation: static void EndSession(ITwitterAuthorizer auth)
{
using (var twitterCtx = new TwitterContext(auth, "https://api.twitter.com/1/", "https://search.twitter.com/"))
{
try
{
//Log
twitterCtx.Log = Console.Out;
var status = twitterCtx.EndAccountSession();
Console.WriteLine("Request: {0}, Error: {1}"
, status.Request
, status.Error);
}
catch (TwitterQueryException tqe)
{
var webEx = tqe.InnerException as WebException;
if (webEx != null)
{
var webResp = webEx.Response as HttpWebResponse;
if (webResp != null && webResp.StatusCode == HttpStatusCode.Unauthorized)
Console.WriteLine("Twitter didn't recognize you as having been logged in. Therefore, your request to end session is illogical.\n");
}
var status = tqe.Response;
Console.WriteLine("Request: {0}, Error: {1}"
, status.Request
, status.Error);
}
}
}
As expected, LINQ to Twitter wraps the exception in a TwitterQueryException as the InnerException. The TwitterQueryException serves a very useful purpose through it's Response property. Notice in the example above that the response has Request and Error proprieties. These properties correspond to the information that Twitter returns as part of it's response payload. This is often useful while debugging to help you understand why Twitter was unable to perform the requested action. Other times, it's cryptic, but that's another story. At least you have some way of knowing in your code how to anticipate and handle these situations, along with having extra information to debug with.
To sum things up, there are two points to make: when and why an exception should be raised and when to wrap and re-throw an exception in a custom exception type. I felt it was necessary to allow the exception to be raised because the called method was unable to perform the task it was designed for. I also felt that it is inappropriate for a general library to do anything with exceptions because that could potentially hide a problem from the caller. A related point is that it should be the exclusive decision of the application that uses the library on what to do with an exception. Another aspect of this situation is that I wrapped the exception in a custom exception and re-threw. This is a tough call because I don’t want to hide any stack trace information. However, the need to make the exception more meaningful by including vital information returned from Twitter swayed me in the direction to design an interface that was as helpful as possible to library consumers. As shown in the code above, you can dig into the exception and pull out a lot of good information, such as the fact that the underlying HTTP response was a 401 Unauthorized. In all, trade-offs are seldom perfect for all cases, but combining the fact that the method was unable to perform its intended function, this is a library, and the extra information can be more helpful, it seemed to be the better design.
@JoeMayo