We are using Ajax call across the application- trying to find out a global solution to redirect to login page if session is already expired while trying to execute any Ajax request. I have coded following solution taking help from this post - Handling session timeout in ajax calls
NOT SURE WHY IN MY CARE EVENT "HandleUnauthorizedRequest" DOES NOT GET FIRED.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CheckSessionExpireAttribute :AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content("/Default.aspx");
filterContext.HttpContext.Session.RemoveAll();
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.HttpContext.Response.Redirect(loginUrl, false);
filterContext.Result = new EmptyResult();
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Using Above custom attribute as follow in controller action:
[NoCache]
[CheckSessionExpire]
public ActionResult GetSomething()
{
}
AJAX Call(JS part):
function GetSomething()
{
$.ajax({
cache: false,
type: "GET",
async: true,
url: "/Customer/GetSomething",
success: function (data) {
},
error: function (xhr, ajaxOptions, thrownError) {
}
}
Web Config Authentication settings:
<authentication mode="Forms">
<forms loginUrl="default.aspx" protection="All" timeout="3000" slidingExpiration="true" />
</authentication>
I am try to check it by deleting browser cooking before making ajax call but event "CheckSessionExpireAttribute " does not get fired- any idea please.
Thanks,
@Paul
If I got the question right (and even if I didn't, thanks anyway, helped me solve my own situation), what you wanted to avoid was having your login page to load inside an element which was supposed to display a different View via Ajax. That or get an exception/error status code during a Ajax form post.
So, in short, the annotation class will need to override 2 methods, not just HandleUnauthorizedRequest
, and it will redirect to a JsonResult Action that will generate the parameters for your Ajax function to know what to do.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class SessionTimeoutAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
IPrincipal user = filterContext.HttpContext.User;
base.OnAuthorization(filterContext);
if (!user.Identity.IsAuthenticated) {
HandleUnauthorizedRequest(filterContext);
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "AccountController", action = "Timeout" }));
}
}
}
Then set this annotation in your authentication Action, so every time it gets called, it will know where the request came from, and what kind of return it should give.
[AllowAnonymous]
[SessionTimeout]
public ActionResult Login() { }
Then your redirected Json Action:
[AllowAnonymous]
public JsonResult Timeout()
{
// For you to display an error message when the login page is loaded, in case you want it
TempData["hasError"] = true;
TempData["errorMessage"] = "Your session expired, please log-in again.";
return Json(new
{
@timeout = true,
url = Url.Content("~/AccountController/Login")
}, JsonRequestBehavior.AllowGet);
}
Then in your client function (I took the privilege of writing it as $.get()
instead of $.ajax()
:
$(document).ready(function () {
$("[data-ajax-render-html]").each(function () {
var partial = $(this).attr("data-ajax-render-html");
var obj = $(this);
$.get(partial, function (data) {
if (data.timeout) {
window.location.href = data.url;
} else {
obj.replaceWith(data);
}
}).fail(function () {
obj.replaceWith("Error: It wasn't possible to load the element");
});
});
});
This function replaces the html tag with this data-ajax-render-html
attribute, which contains the View address you want to load, but you can set it to be loaded inside the tag by changing replaceWith
for the html()
property.