Introduction
This article presents an example to use jQuery global AJAX event handlers.
Background
jQuery provides a list of global AJAX event handlers.
These global events are fired on each AJAX request if the global
property in jQuery.ajaxSetup()
is true, which it is by default.
.ajaxStart
- Register a handler to be called when the first AJAX request begins; .ajaxSend
- Attach a function to be executed before an AJAX request is sent; .ajaxError
- Register a handler to be called when AJAX requests complete with an error;-
.ajaxSuccess
- Attach a function to be executed whenever an AJAX request completes successfully; -
.ajaxComplete
- Register a handler to be called when AJAX requests complete; -
.ajaxStop
- Register a handler to be called when all AJAX requests have completed.
It is ideal to put some operations that are common to all AJAX requests in these event handlers to simplify the implementation of each request. In this article, I will show you how to use these event handlers to achieve the following:
- Display a busy icon and block the UI whenever an AJAX request is in process;
- Display an error message whenever an AJAX error is encountered;
- Re-direct the user to the login page if the web session expires when an AJAX request is made.
The attached
project is an MVC 3 application developed in Visual Studio 2010.
This example application uses the following
JavaScript libraries:
- jquery-1.7.2.min.js - The latest version
of jQuery when the example was developed.
- jquery.blockUI-2.39.js - The jQuery BlockUI plugin. This plug-in is used to display the busy icon and block the UI while an AJAX request is in process.
- jquery.colorbox-min.js - The "ColorBox" plug-in. This plugin is used to display
a message when an AJAX error is encountered.
Besides these
JavaScript libraries, the example application has the following major components:
- The AjaxGlobalHandler.js file is the place where the jQuery global event handlers are used;
- The
Login\Index.cshtml file is the web page where users can log in to the web application;
- The
Home\Index.cshtml file is the web page where the functions
implemented in the AjaxGlobalHandler.js file are used;
- The ActionFilters\AjaxSessionActionFilter.cs file implements an Action Filter. It will be used to check the user's log-in session. If the session expires, it will either re-direct the web page to the log-in page or send the predefined status code to the caller depending on whether the controller action is accessed by an AJAX request.
- The LoginController.cs and HomeController.cs files are the controller for the two web pages.
I will first introduce the AjaxGlobalHandler.js file and the Action Filter implemented in the ActionFilters\AjaxSessionActionFilter.cs file.
I will then show you how to use them in the web pages.
The "AjaxGlobalHandler.js" File
The AjaxGlobalHandler.js file is a very simple JavaScript file, which is implemented as the follows:
var AjaxGlobalHandler = {
Initiate: function (options) {
$.ajaxSetup({ cache: false });
$(document).ajaxStart(function () {
$.blockUI({
message: options.AjaxWait.AjaxWaitMessage,
css: options.AjaxWait.AjaxWaitMessageCss
});
}).ajaxSend(function (e, xhr, opts) {
}).ajaxError(function (e, xhr, opts) {
if (options.SessionOut.StatusCode == xhr.status) {
document.location.replace(options.SessionOut.RedirectUrl);
return;
}
$.colorbox({ html: options.AjaxErrorMessage });
}).ajaxSuccess(function (e, xhr, opts) {
}).ajaxComplete(function (e, xhr, opts) {
}).ajaxStop(function () {
$.unblockUI();
});
}
};
Although jQuery has six global AJAX events, to achieve the goals of this example, I only used the .ajaxStart
, .ajaxError
,
and .ajaxStop
events. In this example, I provided an empty handler for each of the rest of the events. The purpose of the empty handlers is simply keeping a record here,
in case we may need to use them in some other projects later.
- In the
.ajaxSetup
event, the BlockUI is initiated to show the user that an AJAX request is in process. It also blocks the possible user interactions. - In the
.ajaxError
event, the code checks if the error is because of the web session is expired. If it is, it re-directs the page to the log in page.
Otherwise, an error message is shown in the ColorBox. - In the
.ajaxStop
event, the BlockUI is removed to tell the user that the AJAX request has completed.
Before showing you how to use this small
JavaScript file, I will introduce to you the Action Filter first.
The "AjaxSessionActionFilter.cs" File
The AjaxSessionActionFilter.cs file implements an Action Filter:
using System.Web.Mvc;
using AjaxGlobalEventExample.Constants;
namespace AjaxGlobalEventExample.ActionFilters
{
public class AjaxSessionActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var response = filterContext.HttpContext.Response;
var session = filterContext.HttpContext.Session;
if (session[SessionVariables.IsLoggedIn] == null)
{
if (request.IsAjaxRequest())
{
response.StatusCode = 590;
}
else
{
var url = new UrlHelper(filterContext.HttpContext.Request.RequestContext);
response.Redirect(url.Action("Index", "Login"));
}
filterContext.Result = new EmptyResult();
}
base.OnActionExecuting(filterContext);
}
}
}
When configured properly, this Action Filter will be executed before the controller actions. If the web session does not exist and,
- If the request is an AJAX request, it will send the status code 590 to the client;
- If the request is a regular web request, it will re-direct the page to the login page.
The status code 590 is a custom defined status code.
By sending this code to the client, the client will recognize that the web session does not exist.
The Login Page and the Controller
The Login\Index.cshtml file is implemented as follows:
@{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<style type="text/css">
.outerdiv {width: 100%;}
.innerdiv {width: 250px; margin-left: auto; margin-right: auto;}
</style>
<script type="text/javascript">
function Login() {
document.location.replace('@Url.Action("LoginAction", "Login")');
}
</script>
</head>
<body>
<div class="outerdiv">
<div class="innerdiv">
<table style="margin: 0px">
<tr><td style="text-align: center">Everyone is welcome to login</td></tr>
<tr>
<td style="text-align: center">
<a href="#" class="CoolButton" style="width: 200px"
onclick="return Login()">Click to log in</a>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>
This cshtml page has only one button. Clicking on this
button will call the LoginAction
action method implemented in the
LoginController.cs file:
using System.Web.Mvc;
using AjaxGlobalEventExample.Constants;
namespace AjaxGlobalEventExample.Controllers
{
public class LoginController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult LoginAction()
{
Session[SessionVariables.IsLoggedIn] = true;
return new RedirectResult(Url.Action("Index", "Home"));
}
}
}
For simplicity, the LoginAction
method does not require any user credential. It simply initiates the web session. This means that any one will be able to log
in to the web application. Once the web session is established, the user is re-directed to the
Home\Index.cshtml page.
The "Home\Index.cshtml" Page and the Controller
The
Home\Index.cshtml page is implemented as follows:
@{ Layout = null; }
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Global Ajax Event Handler</title>
<link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/ColorBox/colorbox.css")"
rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery/jquery-1.7.2.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ColorBox/jquery.colorbox-min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery/jquery.blockUI-2.39.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/AjaxGlobalHandler.js")"
type="text/javascript"></script>
<script type="text/javascript">
var waitimageUrl = '@Url.Content("~/Content/images/Wait.gif")';
var sessionoutRedirect = '@Url.Action("Index", "Login")';
$(document).ready(function () {
var options = {
AjaxWait: {
AjaxWaitMessage: "<img style='height: 40px' src='"
+ waitimageUrl + "' />",
AjaxWaitMessageCss: { width: "50px", left: "45%" }
},
AjaxErrorMessage: "<h6>Error! please contact the monkey!</h6>",
SessionOut: {
StatusCode: 590,
RedirectUrl: sessionoutRedirect
}
};
AjaxGlobalHandler.Initiate(options);
});
</script>
<script type="text/javascript">
var urlSuccess = '@Url.Action("ExampleAjaxAction", "Home")';
var urlError = '@Url.Action("ExampleAjaxError", "Home")';
var urlClearSession = '@Url.Action("ExampleAjaxClearSession", "Home")';
$(document).ready(function () {
$("#btnSuccessAjax").click(function () {
$.ajax({
url: urlSuccess,
success: function (data) {
$("#divMessage").html(data.Message);
}
});
});
$("#btnErrorAjax").click(function () {
$.ajax({ url: urlError });
});
$("#btnClearSession").click(function () {
$.ajax({
url: urlClearSession,
success: function (data) {
$("#divMessage").html(data.Message);
}
});
});
});
</script>
</head>
<body>
<div id="divMessage" style="font-weight: bold; color: green"></div>
<div style="margin-top: 5px">
<a href="#" class="CoolButton" id="btnSuccessAjax">Success Call</a>
<a href="#" class="CoolButton" id="btnErrorAjax">Error Call</a>
<a href="#" class="CoolButton" id="btnClearSession">Clear Session</a>
</div>
</body>
</html>
This page references the AjaxGlobalHandler.js file. The jQuery global event handlers are initiated in the
$(document).ready()
event. The handlers are initiated with the following information:
- What image we want to show when an AJAX request is in process;
- What message we want to display when an error occurs during an AJAX call;
- Which page to re-direct the user to when the web session expires.
This page also has three buttons. Clicking on each button will make an AJAX call
to the corresponding action method implemented in the HomeController.cs class:
using System;
using System.Threading;
using System.Web.Mvc;
using AjaxGlobalEventExample.ActionFilters;
namespace AjaxGlobalEventExample.Controllers
{
[AjaxSessionActionFilter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ExampleAjaxAction()
{
Thread.Sleep(500);
string msg = "Ajax call succeeded @ " + DateTime.Now.ToLongTimeString();
return Json(new { Message = msg }, JsonRequestBehavior.AllowGet);
}
public ActionResult ExampleAjaxError()
{
Thread.Sleep(500);
Response.StatusCode = 500;
Response.End();
return new EmptyResult();
}
public ActionResult ExampleAjaxClearSession()
{
Thread.Sleep(500);
Session.Clear();
string msg = "Web session cleared @ " + DateTime.Now.ToLongTimeString();
return Json(new { Message = msg }, JsonRequestBehavior.AllowGet);
}
}
}
- The
ExampleAjaxAction
method simply returns a JSON object telling that the AJAX request is successful; - The
ExampleAjaxError
method sends the famous status code 500 to the client indicating an AJAX error; - The
ExampleAjaxClearSession
method clears the web session.
I artificially added a half second delay in each action method so that the web application can better show us the AJAX request process.
You should also have noticed that this controller class is annotated with the
AjaxSessionActionFilter
Action Filter that has been implemented earlier.
Run the Application
Now we complete this example web application and we can test run it.
When the application starts, since no web session exists, the user is re-directed to the log-in page. If we click on the "Click to log in" button, the web session is established and we come to the application's main page.
If we click on the "Success Call" button, an AJAX call is made and we can see the UI is blocked and the busy icon is shown.
If we click on the "Error Call" button, an artificial AJAX error occurs, and the error message is shown up.
We can click on the "Clear Session" button to clear the web session.
Since we have cleared the web session, clicking on any of the buttons will re-direct us to the log-in page.
Points of Interest
- This article presents an example to use jQuery global AJAX event handlers.
- You can use these global event handlers in many ways. In this example, I only show
a very simple usage to address some of the very common issues to simplify AJAX calls.
- If you have time, you can well extend the AjaxGlobalHandler.js file into a jQuery plugin. In this article, I did not worry about this extension, and we have already had a lot of jQuery plugins anyway.
- This example also shows us how to use Action Filters to simplify MVC programming.
- Ideally you should reference and initiate AjaxGlobalHandler.js in the master page so its functionalities are available to every page in your application. In this example,
I simply put it into the web page itself to avoid the complexity involved in master pages.
- In your application, you may find cases when you do not want these global events to fire. In these cases, you can simply set "
global: false
" in your implementation
of AJAX requests. - I hope you like my postings and I hope this article can help you one way or the other.
History
- First revision - 5/11/2012.