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 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 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
1)
We are treating all type of
Exception as Internal Exception which is not true in reality.
2)
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 have 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.
No comments:
Post a Comment