Classic ASP.NET
On classic ASP.NET when calling a [WebMethod] with Ajax in an unauthorized context (most likely in the case where the session expired) the response http status code is 401. You can handle this in the “error” handler (provided by most Ajax frameworks) and redirect to login page. For example I use this code with jQuery:
WebMethod;
[WebMethod] public static MyModel GetStuff() { return new MyModel(); }
Javascript:
$.ajax({ type: "POST", url: "Default.aspx/GetStuff", data: "{}", contentType: "application/json; charset=utf-8", dataType: "json", success: function(msg) { // do stuff }, error: function(xhr, status, ex) { if (xhr.status == 401) // unauthorized { window.location = "Login.aspx?ReturnUrl=" + window.location.pathname; } } });
ASP.NET MVC
To do the same in ASP.NET MVC I have this method in my controller:
[Authorize] public ActionResult GetStuff() { return Json(new MyModel()); }
The ASP.NET MVC infrastructure doesn’t return 401 http status code on failed authorization but 302 http status code (actually the 401 status code is returned initially but later, in the same request, is intercepted by the infrastructure and replaced by 302 status code). XMLHttpRequest object handles this internally automatically following the redirect (no event is fired client side). The Ajax call will end in “success” but the message won’t be the expected JSON but the html of the login page.
The best (well, it’s a hack, if you a better way please tell me) way I found is to replace 302 status code by 401 status code on request end. I added the following code to Global.asax :
protected void Application_EndRequest() { if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest") { Context.Response.Clear(); Context.Response.StatusCode = 401; } }
And the client side code (using Ext JS this time) is:
Ext.Ajax.on('requestexception', function(conn, response, options) { if (response.status == 401) { window.location = '<%= Html.ActionUrl("Account", "LogOn") %>?ReturnUrl=' + window.location.pathname; } });