javascript - كيفية إدارة طلب إعادة توجيه بعد استدعاء jQuery Ajax




redirect (20)

أخيرًا ، أقوم بحل المشكلة عن طريق إضافة HTTP Header مخصص. قبل الرد على كل طلب في جانب الخادم ، أضف عنوان url المطلوب الحالي إلى عنوان الرد.

My application type on server is Asp.Net MVC , and it has a good place to do it. in Global.asax i implemented the Application_EndRequest event so:

    public class MvcApplication : System.Web.HttpApplication
    {

    //  ...
    //  ...

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
        }

    }

It works perfect for me! Now in every response of the JQuery $.post i have the requested url and also other response headers which comes as result of POST method by status 302 , 303 ,... .

and other important thing is that there is no need to modify code on server side nor client side.

and the next is the ability to get access to the other information of post action such errors, messages, and ..., In this way.

I posted this, maybe help someone :)

أنا أستخدم $.post() لاستدعاء servlet باستخدام Ajax ثم استخدام جزء HTML الناتج لاستبدال عنصر div في الصفحة الحالية للمستخدم. ومع ذلك ، إذا انتهت مهلة الجلسة ، يرسل الخادم توجيهًا لإعادة التوجيه لإرسال المستخدم إلى صفحة تسجيل الدخول. في هذه الحالة ، يستبدل jQuery عنصر div بمحتويات صفحة تسجيل الدخول ، مما يجبر المستخدم على مشاهدة مشهد نادر بالفعل.

كيف يمكنني إدارة توجيه إعادة التوجيه من مكالمة أجاكس باستخدام jQuery 1.2.6؟


أردت فقط التمسك بأي طلبات ajax للصفحة بأكملها. SuperG حصلت لي بدأت. إليك ما انتهى به الأمر:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

كنت أرغب بالتحقق بشكل خاص من رموز حالة http معينة لتبني قراري. ومع ذلك ، يمكنك فقط ربط ajaxError للحصول على أي شيء آخر غير النجاح (200 ربما فقط؟) كان بإمكاني كتابة ما يلي:

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

أعتقد أن أفضل طريقة للتعامل مع هذا هو الاستفادة من رموز استجابة بروتوكول HTTP الموجودة ، وتحديدًا 401 Unauthorized .

هنا كيف حللت ذلك:

  1. جانب الخادم: إذا انتهت صلاحية الجلسة ، وكان الطلب هو ajax. أرسل رمزًا لرمز الاستجابة 401
  2. جانب العميل: ربط أحداث ajax

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });
    

IMO هذا هو أكثر عمومية وكنت لا تكتب بعض المواصفات / رأس مخصص جديد. يجب ألا تضطر إلى تعديل أي من مكالمات Ajax الحالية.

تعديل: تعليق @ Rob @ أدناه ، يجب أن يكون 401 (رمز حالة HTTP لأخطاء المصادقة) هو المؤشر. راجع 403 Forbidden vs 401 ردود HTTP غير المصرح بها لمزيد من التفاصيل. مع ذلك ، تستخدم بعض أطر الويب 403 لكل من أخطاء المصادقة والتخويل - لذا يجب التهيئة وفقًا لذلك. شكرا روب.


أعلم أن هذا الموضوع قديم ، لكنني سأعطي منهجًا آخر وجدته وسبق وصفه here . أساسا أنا أستخدم ASP.MVC مع WIF (لكن هذا ليس مهمًا حقًا لسياق هذا الموضوع - الإجابة كافية بغض النظر عن الأطر المستخدمة. يبقى الدليل بدون تغيير - التعامل مع المشكلات المتعلقة بفشل المصادقة أثناء تنفيذ طلبات ajax ) .

يمكن تطبيق النهج المبين أدناه على جميع طلبات ajax من خارج الصندوق (إذا لم يعاد تعريفها قبل إرسال الحدث بوضوح).

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

قبل تنفيذ طلب ajax يتم CheckPulse أسلوب CheckPulse (طريقة التحكم التي يمكن أن تكون أي شيء أبسط):

[Authorize]
public virtual void CheckPulse() {}

في حالة عدم مصادقة المستخدم (انتهت صلاحية الرمز المميز) ، لا يمكن الوصول إلى هذه الطريقة (محمي بواسطة سمة Authorize ). لأن الإطار يعالج المصادقة ، بينما تنتهي صلاحية الرمز المميز ، فإنه يضع حالة http 302 للاستجابة. إذا كنت لا ترغب في أن يتعامل متصفحك مع استجابة 302 بشفافية ، فيمكنك التقاطه في Global.asax وتغيير حالة الاستجابة - على سبيل المثال 200 OK. بالإضافة إلى ذلك ، قم بإضافة رأس ، الذي يرشدك لمعالجة هذه الاستجابة بطريقة خاصة (لاحقًا في جانب العميل):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

أخيرًا ، تحقق جانب العميل من هذا العنوان المخصص. إذا كان موجوداً - يجب أن تتم إعادة التوجيه الكامل إلى صفحة تسجيل الدخول (في حالتي window.location يتم استبداله بـ url من الطلب الذي تتم معالجته تلقائيًا بواسطة إطار العمل الخاص بي).

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}

أنا حل هذا عن طريق وضع ما يلي في صفحة login.php بلدي.

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>

إذا كنت تريد أيضًا تمرير القيم ، فيمكنك أيضًا تعيين متغيرات الجلسة والوصول إلى Eg: في jsp ، يمكنك الكتابة

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

ثم يمكنك تخزين هذه القيمة المؤقتة في متغير جافا سكريبت والتشغيل


الحل الذي تم تنفيذه في نهاية الأمر هو استخدام غلاف لوظيفة رد الاتصال لمكالمة Ajax وفي هذا الفحص ، للكشف عن وجود عنصر محدد في جزء HTML المرتجع. إذا تم العثور على العنصر ثم نفذ المجمع إعادة توجيه. إذا لم يكن الأمر كذلك ، فقد قام المجمع بإحالة المكالمة إلى وظيفة رد الاتصال الفعلية.

على سبيل المثال ، كان لدينا وظيفة المجمع شيء من هذا القبيل:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

ثم ، عند إجراء مكالمة Ajax استخدمنا شيئًا مثل:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

وقد نجح هذا الأمر نظرًا لأن جميع مكالمات Ajax دأبت على إرجاع HTML دائمًا داخل عنصر DIV نستخدمه لاستبدال جزء من الصفحة. أيضا ، نحن بحاجة فقط لإعادة التوجيه إلى صفحة تسجيل الدخول.


تستخدم معظم الحلول المقدمة حلًا ، باستخدام رأس إضافي أو رمز HTTP غير مناسب. هذه الحلول ستعمل على الأرجح ولكنها تشعر بقليل من "الإزعاج". لقد توصلت إلى حل آخر.

نحن نستخدم WIF الذي تم تكوينه لإعادة التوجيه (passiveRedirectEnabled = "true") على استجابة 401. تعد عملية إعادة التوجيه مفيدة عند التعامل مع الطلبات العادية ولكنها لن تعمل مع طلبات AJAX (نظرًا لأن المتصفحات لن تنفذ عملية إعادة التوجيه 302 /).

باستخدام التعليمة البرمجية التالية في global.asax لديك ، يمكنك تعطيل إعادة التوجيه لطلبات AJAX:

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

يتيح لك هذا الخيار عرض 401 رد لطلبات AJAX ، والتي يمكن لـ javascript معالجتها من خلال إعادة تحميل الصفحة. سيؤدي إعادة تحميل الصفحة إلى طرح 401 سيتم معالجتها بواسطة WIF (وسيقوم WIF بإعادة توجيه المستخدم إلى صفحة تسجيل الدخول).

مثال لجافا سكريبت لمعالجة الأخطاء 401:

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});

في servlet يجب عليك وضع response.setStatus(response.SC_MOVED_PERMANENTLY); لإرسال الحالة "301" xmlHttp التي تحتاجها لإعادة التوجيه ...

وفي الدالة $ .ajax يجب عدم استخدام الدالة. .toString() ... ، فقط

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

المشكلة هي أنها ليست مرنة جدا ، لا يمكنك أن تقرر أين تريد إعادة توجيه ..

إعادة توجيه من خلال servlets يجب أن يكون أفضل طريقة. ولكن ما زلت لا أستطيع العثور على الطريقة الصحيحة للقيام بذلك.


في حين يبدو أن الإجابات تعمل لصالح الأشخاص إذا كنت تستخدم Spring Security ، إلا أنني وجدت تمديد LoginUrlAuthenticationEntryPoint وإضافة رمز معين للتعامل مع AJAX أكثر قوة. معظم الأمثلة تعترض جميع عمليات إعادة التوجيه وليس فقط إخفاقات المصادقة. كان هذا غير مرغوب فيه للمشروع الذي أعمل عليه. قد تجد الحاجة إلى توسيع ExceptionTranslationFilter وتجاوز طريقة "sendStartAuthentication" لإزالة خطوة التخزين المؤقت إذا كنت لا تريد تخزين طلب AJAX الفاشل مؤقتًا.

مثال AjaxAwareAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
    super(loginUrl);
}

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    if (isAjax(request)) {
        response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
    } else {
        super.commence(request, response, authException);
    }
}

public static boolean isAjax(HttpServletRequest request) {
    return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}

}

المصادر: 1 ، 2


قد يجد البعض المفيد أدناه:

كنت أرغب في إعادة توجيه العملاء إلى صفحة تسجيل الدخول لأي إجراءات راحة يتم إرسالها بدون رمز تفويض. بما أن جميع أعمالي تقوم على أساس Ajax ، احتجت إلى طريقة عامة جيدة لإعادة التوجيه إلى صفحة تسجيل الدخول بدلاً من التعامل مع وظيفة نجاح Ajax.

هذا ما فعلته:

على أي طلب Ajax سيرجع الخادم استجابة Json 200 "يلزم المصادقة" (إذا كان العميل بحاجة إلى المصادقة).

مثال بسيط في Java (جانب الخادم):

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map<String, Cookie> cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

في جافا سكريبت الخاص بي ، قمت بإضافة التعليمة البرمجية التالية:

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

وهذا عن ذلك.


قرأت هذا السؤال ونفذت النهج الذي تم ذكره فيما يتعلق بتعيين رمز حالة الاستجابة إلى 278 لتجنب المتصفح بشكل واضح يتعامل مع عمليات إعادة التوجيه. على الرغم من أن هذا كان ناجحًا ، إلا أنني كنت مستاءًا بعض الشيء حيث إنه نوع من الاختراق.

بعد المزيد من الحفر ، تخليت عن هذا النهج واستخدمت JSON . في هذه الحالة ، تحتوي جميع الردود على طلبات ajax على رمز الحالة 200 ويحتوي نص الاستجابة على كائن JSON تم إنشاؤه على الخادم. بعد ذلك ، يمكن لجافا سكريبت على العميل استخدام كائن JSON لتحديد ما يلزم القيام به.

كان لدي مشكلة مماثلة لك. أقوم بتنفيذ طلب ajax يحتوي على ردين محتملين: أحدهما يعيد توجيه المتصفح إلى صفحة جديدة وأخرى تحل محل نموذج HTML موجود في الصفحة الحالية بأخرى جديدة. رمز jquery للقيام بهذا يبدو كالتالي:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        }
        else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

يتم إنشاء "بيانات" كائن JSON على الخادم للحصول على عضوين: data.redirect و data.form. لقد وجدت هذا النهج ليكون أفضل بكثير.


لدي حل بسيط يعمل بالنسبة لي ، لا حاجة لتغيير رمز الخادم ... فقط إضافة ملعقة صغيرة من جوزة الطيب ...

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "<html>" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

أتحقق من وجود علامة html ، ولكن يمكنك تغيير indexOf للبحث عن أي سلسلة فريدة موجودة في صفحة تسجيل الدخول الخاصة بك ...


لقد تمكنت من حل هذه المشكلة على هذا النحو:

أضف برنامج وسيط لمعالجة الاستجابة ، إذا كانت إعادة توجيه لطلب ajax ، قم بتغيير الاستجابة إلى استجابة عادية باستخدام عنوان URL الخاص بإعادة التوجيه.

class AjaxRedirect(object):
  def process_response(self, request, response):
    if request.is_ajax():
      if type(response) == HttpResponseRedirect:
        r = HttpResponse(json.dumps({'redirect': response['Location']}))
        return r
    return response

ثم في ajaxComplete ، إذا احتوت الاستجابة على إعادة توجيه ، فيجب أن تكون إعادة توجيه ، لذا قم بتغيير موقع المستعرض.

  $('body').ajaxComplete(function (e, xhr, settings) {
    if (xhr.status == 200) {
      var redirect = null;
      try {
        redirect = $.parseJSON(xhr.responseText).redirect;
        if (redirect) {
          window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
        }
      } catch (e) {
        return;
      }

    }

لم يكن لدي أي نجاح مع حل رأس - لم يتم التقاطها في بلدي ajaxSuccess / ajaxComplete طريقة. استخدمت إجابة <ست St> من خلال الرد المخصص ، لكنني قمت بتعديل الجانب JS بعض. أقوم بإعداد طريقة أتصل بها في كل وظيفة حتى أتمكن من استخدام $.get و $.post $.get القياسية.

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

مثال على ذلك قيد الاستخدام ...

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}

محاولة

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

ضعها على صفحة تسجيل الدخول. إذا تم تحميله في div على الصفحة الرئيسية ، فسيعيد توجيه صفحة تسجيل الدخول. "#site" هو معرف لـ div موجود على جميع الصفحات باستثناء صفحة تسجيل الدخول.


وضع ما قاله فلاديمير برودنيكوف وتوماس هانسن:

  • تغيير التعليمات البرمجية من جانب الخادم للكشف عما إذا كان XHR. إذا كان الأمر كذلك ، فقم بتعيين رمز الاستجابة الخاص بإعادة التوجيه إلى 278. في django:
   if request.is_ajax():
      response.status_code = 278

هذا يجعل المتصفح يعامل الاستجابة كنجاح ، ويسلمها إلى جافا سكريبت.

  • في JS ، تأكد من إرسال النموذج عبر Ajax ، تحقق من رمز الاستجابة وأعد التوجيه إذا لزم الأمر:
$('#my-form').submit(function(event){ 

  event.preventDefault();   
  var options = {
    url: $(this).attr('action'),
    type: 'POST',
    complete: function(response, textStatus) {    
      if (response.status == 278) { 
        window.location = response.getResponseHeader('Location')
      }
      else { ... your code here ... } 
    },
    data: $(this).serialize(),   
  };   
  $.ajax(options); 
});

Additionally you will probably want to redirect user to the given in headers URL. So finally it will looks like this:

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD: Opps. Have the same task, but it not works. Doing this stuff. I'll show you solution when I'll find it.


I was having this problem on a django app I'm tinkering with (disclaimer: I'm tinkering to learn, and am in no way an expert). What I wanted to do was use jQuery ajax to send a DELETE request to a resource, delete it on the server side, then send a redirect back to (basically) the homepage. When I sent HttpResponseRedirect('/the-redirect/') from the python script, jQuery's ajax method was receiving 200 instead of 302. So, what I did was to send a response of 300 with:

response = HttpResponse(status='300')
response['Location'] = '/the-redirect/' 
return  response

Then I sent/handled the request on the client with jQuery.ajax like so:

<button onclick="*the-jquery*">Delete</button>

where *the-jquery* =
$.ajax({ 
  type: 'DELETE', 
  url: '/resource-url/', 
  complete: function(jqxhr){ 
    window.location = jqxhr.getResponseHeader('Location'); 
  } 
});

Maybe using 300 isn't "right", but at least it worked just like I wanted it to.

PS :this was a huge pain to edit on the mobile version of SO. Stupid ISP put my service cancellation request through right when I was done with my answer!


You can also hook XMLHttpRequest send prototype. This will work for all sends (jQuery/dojo/etc) with one handler.

I wrote this code to handle a 500 page expired error, but it should work just as well to trap a 200 redirect. Ready the wikipedia entry on XMLHttpRequest onreadystatechange about the meaning of readyState.

// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.send = function() {
  //console.dir( this );

  this.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
      try {
        document.documentElement.innerHTML = this.responseText;
      } catch(error) {
        // IE makes document.documentElement read only
        document.body.innerHTML = this.responseText;
      }
    }
  };

  oldXMLHttpRequestSend.apply(this, arguments);
}




redirect