8

我有一个对 MVC 的 ajax 调用,它返回一个局部视图。这一切都很好,直到会话结束或 cookie 过期。当我进行 ajax 调用时,它会在 div 中显示用于局部视图的内容。如何在 ajax 调用期间检测到我的会话已过期并正确重定向到全屏/页面

4

3 回答 3

5

我建议将您的所有请求封装到一个包装器元素中:

public class JsonResponse<T>
{
    public JsonResponse()
    {
    }

    public JsonResponse(T Data)
    {
        this.Data = Data;
    }

    public T Data { get; set; }
    public bool IsValid { get; set; }
    public string RedirectTo { get; set; }
}

您要发送给客户的模型在 Data.xml 中。

为了填充 RedirectTo,我在 Global.Asax 中使用了GlobalAuthorize属性,并为 HandleUnauthorizedRequests 添加了句柄

public sealed class GlobalAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest
      (AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult
            {
                Data = new JsonResponse<bool>
                       {
                           IsValid = false,
                           //RedirectTo = FormsAuthentication.LoginUrl
                           RedirectTo = "/"
                       },
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    } 

此外,我已将所有Ajax 请求封装到一个函数中,该函数检查RedirectTo.

function global_getJsonResult(Controller, View, data, successCallback, completeCallback, methodType)
{
    if (IsString(Controller)
        && IsString(View)
        && !IsUndefinedOrNull(data))
    {
        var ajaxData;
        var ajaxType;

        if (typeof (data) == "string")
        {
            ajaxData = data;
            ajaxType = "application/x-www-form-urlencoded"
        }
        else
        {
            ajaxData = JSON.stringify(data);
            ajaxType = "application/json; charset=utf-8";
        }
        var method = 'POST';

        if (!IsUndefinedOrNull(methodType)) 
        {
            method = methodType;
        }

        var jqXHR = $.ajax({
            url: '/' + Controller + '/' + View,
            data: ajaxData,
            type: method,
            contentType: ajaxType,
            success: function(jsonResult)
            {
                if (!IsUndefinedOrNull(jsonResult)
                    && jsonResult.hasOwnProperty("RedirectTo")
                    && !IsUndefinedOrNull(jsonResult.RedirectTo)
                    && jsonResult.RedirectTo.length > 0)
                {
                    $.fn.notify('error', 'Login Expired', 'You have been inactive for a prolonged period of time, and have been logged out of the system.');
                    window.setTimeout(function() { window.location = jsonResult.RedirectTo }, 5000);
                }
                else if (IsFunction(successCallback))
                {
                    successCallback(jsonResult, Controller + '/' + View);
                }
            },
            error: function(jqXHR, textStatus, errorThrown)
            {
                if (errorThrown != 'abort')
                {
                    $.fn.notify('error', 'AJAX Connection Error', textStatus + ': ' + errorThrown);
                }

            },
            complete: function(jqXHR, textStatus)
            {
                if (IsFunction(completeCallback))
                {
                    completeCallback(jqXHR, textStatus, Controller + '/' + View);
                }
            }
        });

        return jqXHR;
    }
}
于 2012-05-14T20:39:56.717 回答
4

您可以使用 javascript 在客户端上创建一个计时器,该计时器将在会话超时时向用户显示一个对话框。您只需将计时器的值设置为会话超时。然后在ajax请求时,它也会重置倒计时。

var g_sessionTimer = null;
function uiSessionInit() {
    id = "uiTimeout";
    timeout = 3600000 * 24; // 1 day timeout
    uiSessionSchedulePrompt(id, timeout);
    $('body').ajaxStart(function () {
        // reset timer on ajax request
        uiSessionSchedulePrompt(id, timeout);
    });
}
function uiSessionSchedulePrompt(id, timeout) {
    if (g_sessionTimer)
        clearTimeout(g_sessionTimer);
    g_sessionTimer = setTimeout(function () { uiSessionExpiring(id); }, timeout);
}
function uiSessionExpiring(id) {
    // create a dialog div and use this to show to the user
    var dialog = $('<div id="uiTimeout"></div>').text("Your session with has timed out. Please login again.");
    $('body').append(dialog);
    $('#uiTimeout').dialog({ 
           autoOpen: true, 
           modal: true, 
           title: 'Expired Session', 
           buttons: { 
                "OK": function(){
                    $(this).dialog('close'); 
                }
           },
           close: uiSessionDialogClose
     });
}

function uiSessionDialogClose(){
    // take user to sign in location
    location = 'http://www.mypage.com'; 
}
于 2012-05-14T20:39:47.307 回答
0

已经 8 年了,但我刚刚遇到了一个类似的问题,即在用户的身份验证 cookie 过期后进行的将部分视图加载到模式中的 Ajax 调用会导致完整的登录页面被加载到模式中。这就是我解决它的方法。

首先,我在登录页面上添加了一个隐藏控件。value不是严格要求的,但我认为它使标记更具可读性:

<input id="isLoginPage" type="hidden" value="true" />

然后我像这样修改了 Ajax 调用。我正在使用 jQuery,但同样的原理也适用于普通的旧 Javascript:

$.ajax({
    url: `/mypartialview`,
    type: "GET",
    success: function (data) {
        var $modal = $("#myModal");
        $modal.find(".modal-content").html(data);
        if ($modal.find("#isLoginPage").length > 0) {
            window.location.replace("/");
        }
        $modal.modal("show");
    }
});

这样做的基础是,如果用户的身份验证已过期,MVC 的默认行为是返回登录页面,所以我“嗅探”Ajax 调用返回的isLoginPage隐藏输入视图,如果找到,我知道登录页面已经返回,所以我只是将用户重定向到应用程序的根目录,然后 MVC 将重定向到主登录页面。

如果您的应用程序的根允许匿名访问,您可以替换window.location.replace("/")为应用程序登录页面的路径,例如window.location.replace("/account/login").

通过这项工作,我将解决方案封装在一个函数中以简化事情并避免重复自己:

function handleAjaxData($container, data) {
    $container.html(data);
    if ($container.find("#isLoginPage").length > 0) {
        window.location.replace("/");
    }
}

然后我可以在我的 Ajax 调用中使用它,如下所示:

$.ajax({
    url: `/mypartialview`,
    type: "GET",
    success: function (data) {
        var $modal = $("#myModal");
        handleAjaxData($modal.find(".modal-content"), data);
        $modal.modal("show");
    }
});

您显然可以更进一步,将 Ajax 调用本身包含在函数中,但在我的情况下,这将涉及一些复杂的回调,所以我没有打扰。

于 2020-07-16T11:58:35.220 回答