One thing to keep in mind when using ASP.NET MVC is how to gracefully handle exceptions that are thrown when executing Ajax requests. Normally if an error is thrown on the server, without proper error handling, the client will never know about the error. Another thing I wanted to do was not to litter my action methods with try/catch/finally blocks. They're good, but I want my action methods to look elegant! By the end of this article, you'll be able to decorate your action method's that return a JsonResult with the following attribute:
C#
[HttpGet]
[HandleJsonException]
public JsonResult ThrowAjaxError()
{
throw new Exception("An error has occured in this method");
}
VB.NET (Converted)
<HttpGet, HandleJsonException> _
Public Function ThrowAjaxError() As JsonResult
Throw New Exception("An error has occured in this method")
End Function
The thing I really love about ASP.NET MVC is the ability to use ActionFilters. These give you the ability to perform different actions on the HTTP request before or after the action is executed. By making the most of ActionFilters, your code will be more readable instantly.
Ok let's get started! I'm using ASP.NET MVC 2 and Visual Studio 2010. If you haven't got it you can download it from here. The one thing you need to do when creating your own custom action filter is your class needs to derive from ActionFilterAttribute. After that you need to override the method you want to use. Here's the complete code:
C#
public class HandleJsonExceptionAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
filterContext.Exception.Message,
filterContext.Exception.StackTrace
}
};
filterContext.ExceptionHandled = true;
}
}
}
VB.NET (Converted)
Public Class HandleJsonExceptionAttribute
Inherits ActionFilterAttribute
Public Overrides Sub OnActionExecuted(ByVal filterContext As ActionExecutedContext)
If filterContext.HttpContext.Request.IsAjaxRequest() AndAlso filterContext.Exception IsNot Nothing Then
filterContext.HttpContext.Response.StatusCode = CInt(Fix(System.Net.HttpStatusCode.InternalServerError))
filterContext.Result = New JsonResult() With {.JsonRequestBehavior = JsonRequestBehavior.AllowGet, .Data = New With {Key filterContext.Exception.Message, Key filterContext.Exception.StackTrace}}
filterContext.ExceptionHandled = True
End If
End Sub
End Class
Ok let's go through what's happening in the code above. I'm deriving from the ActionFilterAttribute class. This allows me to override the OnActionExecuted method. The only argument to this method is ActionExecutedContext, which holds everything I need to know about the request:
C#
public override void OnActionExecuted(ActionExecutedContext filterContext)
VB.NET (Converted)
public override void OnActionExecuted(ActionExecutedContext filterContext)
I only want this code to handle exceptions where the client is expecting a JSON result. The HttpRequestBase class has a nice method called IsAjaxRequest that does that for me:
C#
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
VB.NET (Converted)
If filterContext.HttpContext.Request.IsAjaxRequest() AndAlso filterContext.Exception IsNot Nothing Then
Next I set the StatusCode to reflect an internal error:
C#
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
VB.NET (Converted)
filterContext.HttpContext.Response.StatusCode = CInt(Fix(System.Net.HttpStatusCode.InternalServerError))
The next line replaces the result of the action method. I'm instructing the action to return a JsonResult, but with the exception information:
C#
filterContext.Result = new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
filterContext.Exception.Message,
filterContext.Exception.StackTrace
}
};
VB.NET (Converted)
filterContext.Result = New JsonResult() With {.JsonRequestBehavior = JsonRequestBehavior.AllowGet, .Data = New With {Key filterContext.Exception.Message, Key filterContext.Exception.StackTrace}}
Finally I let the request know I've handled the exception:
filterContext.ExceptionHandled = true;
To use this new attribute, I've created a new action called ThrowAjaxError. Obviously this is only a good naming convention for this article! I can decorate the action with the HandleJsonException attribute now:
C#
[HttpGet]
[HandleJsonException]
public JsonResult ThrowAjaxError()
{
throw new Exception("An error has occurred in this method");
}
VB.NET (Converted)
<HttpGet, HandleJsonException> _
Public Function ThrowAjaxError() As JsonResult
Throw New Exception("An error has occurred in this method")
End Function
In the view, I've got the following code to call this action via jQuery's Ajax function:
<form method="get" action="/Home/ThrowAjaxError" id="RunAjaxForm">
<input type="submit" id="RunAjax" value="Run Ajax" />
</form>
<script language="javascript" type="text/javascript">
$(function () {
$("#RunAjaxForm").submit(function (e) {
e.preventDefault();
$.ajax({
type: "GET",
url: this.action,
data: {},
dataType: "json",
success: function (msg) {
alert(msg);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
var msg = JSON.parse(XMLHttpRequest.responseText);
alert(msg.Message);
}
});
});
});
</script>
In the script above, I'm hijacking the form's get request so I can post the form via Ajax. Normally when an exception is thrown in an action and the client is expecting JSON, you get no feedback. This is not the case thanks to the HandleJsonException attribute. The exception will be sent back to the client, and by using a library such as the open source JSON parser, I can parse the request and use it like any other JSON object.
This is one way of implementing this functionality. As with anything there are multiple ways of doing this, but it works for me. The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @
malcolmsheridan