In this article we will implement centralize exception handling mechanism in Web API. I hope all of you know the basic concept of exception handle mechanism in C# like you have good understanding of try-catch and finally block. That’s find they are very much handy in native application (means WinForms and WebForms) but when we will talk about exception handling in serviced based application like Web API, the scenario is little bit different. If we simply implement try-cat and throw exception we will not get proper message from service end when exception will occur.
Let’s implement our traditional try-catch mechanism in Web API
This is the client side implementation. We have used ajax() function of JQuery and calling to “Values” controller which we will host in same solution using GET HTTP verb. We have defined two callback functions to handle response from RESTful Web API. Here is sample code.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Client.aspx.cs" Inherits="MVC4.Client" %>
<head id="Head1" runat="server">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$(document).ready(function () {
$.ajax({
url: 'http://localhost:12849/api/Values',
type: 'GET',
success: function (data, textStatus, xhr) {
console.log(data);
},
error: function (xhr, textStatus, errorThrown) {
console.log(errorThrown);
}
});
});
</script>
</head>
<body>
</body>
</html> That’s fine now we will implement API part and intentionally we will throw Devideby Zero Exception from controller. In below example within GET method we are throwing Exception.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http; namespace MVC4.Controllers
{
public class ValuesController : ApiController
{
public int Get()
{
int a = 100, b = 0;
try
{
return a / b;
}
catch (DivideByZeroException ex)
{
throw new DivideByZeroException("Dont Devide by Zero");
}
}
}
}Now, in output we are seeing that API is throwing “Internal Server error” message but we are pretty sure that that the exception has fire due to invalid mathematical operation. So, the actual exception is suppressed to user and we are getting an exception message which is 5000 feet of actual exception.
Now the question is how we will throw proper exception message from API?
Populate HttpResponseException object and throw it.
Yes, this is the proper solution to throw exception from Web API. We have to populate one object of HttpResponseException class by setting HttpResponseMessage property and we will throw it in time of exception occurrence. Here is code for client application. We did not made any change in client code for the sake of uniformity throughout this article.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Client.aspx.cs" Inherits="MVC4.Client" %>
<head id="Head1" runat="server">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$(document).ready(function () {
$.ajax({
url: 'http://localhost:12849/api/Values',
type: 'GET',
success: function (data, textStatus, xhr) {
console.log(data);
},
error: function (xhr, textStatus, errorThrown) {
console.log(errorThrown);
}
});
});
</script>
</head>
<body>
</body>
</html>Now within catch we will populate object of HttpResponseException and we will throw it. Have a look on below code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http; namespace MVC4.Controllers
{
public class ValuesController : ApiController
{
public int Get()
{
int a = 100, b = 0;
try
{
return a / b;
}
catch (DivideByZeroException ex)
{
var resp = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(string.Format("We cannot devide by zero")),
ReasonPhrase = "We cannot devide by zero"
};
throw new HttpResponseException(resp);
}
}
}
} Implementation is simple but there are two problems in this implementation, the first one is :
- We are treating all type of Exception as Internal Exception which is not true in reality.
- We are populating whole HttpResponseException object within catch block and it will execute when only “DevideByZeroException” will occur. So, if we suspect that one function might throw 5 types of Exception we have to populate “HttpResponseException” object five times within single function which will lead towards bad solution.
Here is output of this example.
Any way this implementation is bad but not wrong, because we are getting our customized Exception message. That’s fine and cool at least we have solved one problem. Now we will solve the second problem which we have mentioned above. We will not implement try-catch in each and every controller; from any central location we will try to handle all Exceptions.
Override OnException() function from ExceptionFilterAttribute class
We can make centralize Exception handling mechanism by overriding onException() function of “ExceptionFilterAttribute” class. In this example we have override onException exception and populating object of “HttpRescponseException” object by setting exception message and HttpStatuscode property.
Just keep this class in Global.asax page of your application.
public class ExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = context.Exception.Message
});
}
} Now, only one task is there, we have to set the class name as attribute over controller and it will release us to implement try-catch in each and every action. When any type of exception will occur, the “onException” function will execute in background. Here is implementation of controller.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http; namespace MVC4.Controllers
{
public class ValuesController : ApiController
{
[ExceptionAttribute]
public int Get()
{
throw new DivideByZeroException();
}
}
}
We did not given client code to reduce redundancy; the client code is same as above. Now, we are seeing that we are getting message relevant to exception type without implementing try-cat within each controller. This is not true only for “Values” controller, we can implement ‘n’ number of controller without try-cat block and “onException” function will take care about all exception. We somehow we have centralize exception handling system.
Now the is one problem in this implementation. We are treating all Exception as InternalException again. There is lack of proper categorization of Exceptions. We will solve this limitation shortly. But before that we will look “onException” function bit closely. How the magic is happening?
When we are setting class name (in this example [ExceptionAttribute]) as attribute of any action MVC framework internally wrap the whole action within try block and put “HttpResponseException” object generation mechanism within catch block. The pseudo code representation is like below.
Try
{
//content of particular controller
}
Catch
{
//HttpResponseException message generation mechanism.
}
Ok, this is the internal operation behind this magic. Now we will solve the issue with exception categorization and for that we will maintain one dictionary to maintain all Exception type and it’s “HttpResponsecode” . Now when exception will come we will check with dictionary entry if found we will populate Exception message with relevant information if not found then we will treat the Exception as anonymous exception. Here is sample implementation.
public class ExceptionAttribute : ExceptionFilterAttribute
{
//Populate Exception message within constructor
public ExceptionAttribute()
{
this.Mappings = new Dictionary<Type, HttpStatusCode>();
this.Mappings.Add(typeof(ArgumentNullException), HttpStatusCode.BadRequest);
this.Mappings.Add(typeof(ArgumentException), HttpStatusCode.BadRequest);
this.Mappings.Add(typeof(DivideByZeroException), HttpStatusCode.BadRequest);
}
public IDictionary<Type, HttpStatusCode> Mappings
{
get;
//Set is private to make it singleton
private set;
}
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Exception != null)
{
var exception = actionExecutedContext.Exception;
string type = exception.GetType().ToString();
if (actionExecutedContext != null)
{
// LookUp Mapping Dictionary to get exception type
if (this.Mappings.ContainsKey(exception.GetType()))
{
//Get Status code from Dictionary
var httpStatusCode = this.Mappings[exception.GetType()];
// Create Message Body with information
throw new HttpResponseException(new HttpResponseMessage(httpStatusCode)
{
Content = new StringContent("Method Access Exception" + actionExecutedContext.Exception.Message),
ReasonPhrase = actionExecutedContext.Exception.Message
});
}
else
{
//Else part executes means there is not information in repository so it is some kind of anonymous exception
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("System is failure to process request"),
ReasonPhrase = "System is failure to process request"
});
}
}
}
}
} We can are able to categorize actual Exception type in application.