Câu hỏi Cách quản lý yêu cầu chuyển hướng sau cuộc gọi jQuery Ajax


Tôi đang sử dụng $.post() gọi một servlet bằng Ajax và sau đó sử dụng đoạn HTML kết quả để thay thế div trong trang hiện tại của người dùng. Tuy nhiên, nếu phiên làm việc hết giờ, máy chủ sẽ gửi chỉ thị chuyển hướng để gửi người dùng đến trang đăng nhập. Trong trường hợp này, jQuery đang thay thế div yếu tố với nội dung của trang đăng nhập, buộc mắt của người dùng phải chứng kiến ​​một cảnh hiếm hoi thực sự.

Làm thế nào tôi có thể quản lý một chỉ thị chuyển hướng từ một cuộc gọi Ajax với jQuery 1.2.6?


1181
2017-10-13 21:29


gốc


(không phải là câu trả lời như vậy) - Tôi đã làm điều này trong quá khứ bằng cách chỉnh sửa thư viện jquery và thêm một kiểm tra cho trang đăng nhập trên mỗi XHR hoàn thành. Không phải là giải pháp tốt nhất bởi vì nó sẽ phải được thực hiện mỗi khi bạn nâng cấp, nhưng nó giải quyết vấn đề. - Sugendran
Xem câu hỏi liên quan: stackoverflow.com/questions/5941933/… - Nutel
Các HttpContext.Response.AddHeader và kiểm tra thành công ajaxsetup là con đường để đi - Lijo
Tại sao máy chủ không thể trả về 401? Trong trường hợp đó, bạn có thể có $ .ajaxSetup toàn cầu và sử dụng mã trạng thái để chuyển hướng trang. - Vishal
liên kết này doanduyhai.wordpress.com/2012/04/21/… mang lại cho tôi giải pháp đúng - pappu_kutty


Các câu trả lời:


Tôi đã đọc câu hỏi này và triển khai phương pháp đã được đề cập về việc thiết lập mã trạng thái phản hồi thành 278 để tránh trình duyệt xử lý rõ ràng các chuyển hướng. Mặc dù điều này có hiệu quả nhưng tôi hơi không hài lòng vì nó là một chút hack.

Sau khi tìm hiểu thêm, tôi đã bỏ qua cách tiếp cận này và sử dụng JSON. Trong trường hợp này, tất cả các câu trả lời cho các yêu cầu ajax có mã trạng thái 200 và phần thân của đáp ứng chứa một đối tượng JSON được xây dựng trên máy chủ. Sau đó, javascript trên máy khách có thể sử dụng đối tượng JSON để quyết định những gì nó cần làm.

Tôi đã có một vấn đề tương tự như của bạn. Tôi thực hiện một yêu cầu ajax có 2 câu trả lời có thể: một câu trả lời chuyển hướng trình duyệt đến một trang mới và một trang thay thế một biểu mẫu HTML hiện có trên trang hiện tại bằng một trang mới. Mã jquery để làm điều này trông giống như sau:

$.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);
        }
    }
});

Đối tượng JSON "dữ liệu" được xây dựng trên máy chủ để có 2 thành viên: data.redirect và data.form. Tôi thấy cách tiếp cận này tốt hơn nhiều.


631
2017-10-07 22:54



Như đã nêu trong giải pháp trong stackoverflow.com/questions/503093/… nó là tốt hơn để sử dụng window.location.replace (data.redirect); hơn window.location.href = data.redirect; - Carles Barrobés
Bất kỳ lý do nào tại sao nó sẽ không tốt hơn để sử dụng các mã HTTP trên hành động. Ví dụ: mã 307 là chuyển hướng tạm thời HTTP? - Sergei Golos
@Sergei Golos lý do là nếu bạn làm một chuyển hướng HTTP, chuyển hướng thực sự không bao giờ đến để gọi lại thành công ajax. Trình duyệt xử lý chuyển hướng phân phối mã 200 với nội dung đích đến của chuyển hướng. - Miguel Silva
Điều này chỉ hoạt động nếu bạn có quyền kiểm soát trên máy chủ - h--n
Câu trả lời này sẽ hữu ích hơn nếu nó đã cho thấy cách thực hiện điều này trên máy chủ. - Pedro Hoehl Carvalho


Tôi đã giải quyết vấn đề này bằng cách:

  1. Thêm tiêu đề tùy chỉnh vào phản hồi:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. Ràng buộc một hàm JavaScript vào ajaxSuccess sự kiện và kiểm tra xem liệu tiêu đề có tồn tại hay không:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    

213
2017-11-20 08:39



Thật là một giải pháp tuyệt vời. Tôi thích ý tưởng về giải pháp một cửa. Tôi cần phải kiểm tra cho một tình trạng 403, nhưng tôi có thể sử dụng liên kết ajaxSuccess trên cơ thể cho điều này (đó là những gì tôi đã thực sự tìm kiếm.) Cảm ơn. - Bretticus
Tôi chỉ làm điều này và thấy rằng tôi cần ajaxComplete nơi tôi đã sử dụng hàm $ .get () và bất kỳ trạng thái nào khác ngoài 200 không kích hoạt. Trong thực tế, tôi có thể có lẽ chỉ cần ràng buộc để ajaxError thay thế. Xem câu trả lời của tôi dưới đây để biết thêm chi tiết. - Bretticus
Nó có thể được chấp nhận trong nhiều trường hợp, nhưng nếu khung của bạn xử lý ủy quyền thì sao? - jwaliszko
Tôi thích cách tiếp cận tiêu đề nhưng cũng nghĩ - giống như @ mwoods79 - rằng kiến ​​thức về nơi để chuyển hướng đến không nên trùng lặp. Tôi đã giải quyết điều đó bằng cách thêm một tiêu đề REDIRECT_LOCATION thay vì một boolean. - rintcius
Hãy cẩn thận để đặt tiêu đề trên phản hồi SAU chuyển hướng. Như được mô tả trong các câu trả lời khác trên trang này, chuyển hướng có thể trong suốt đối với trình xử lý ajaxSucces. Vì vậy, tôi đã đưa tiêu đề vào phản hồi GET của trang đăng nhập (cuối cùng và chỉ kích hoạt ajaxSuccess trong kịch bản của tôi). - sieppl


Không có trình duyệt nào xử lý đúng 301 và 302 phản hồi. Và trên thực tế, tiêu chuẩn thậm chí còn nói rằng họ nên xử lý chúng "minh bạch" là một nhức đầu MASSIVE cho các nhà cung cấp Thư viện Ajax. Trong Ra-Ajax chúng tôi đã bị buộc phải sử dụng mã trạng thái phản hồi HTTP 278 (chỉ một số mã thành công "không được sử dụng") để xử lý chuyển hướng trong suốt từ máy chủ ...

Điều này thực sự làm phiền tôi, và nếu ai đó ở đây có một số "kéo" trong W3C tôi sẽ đánh giá cao rằng bạn có thể cho W3C biết chúng tôi thực sự cần xử lý 301 mã số 302 và chính mình ...! ;)


106
2018-01-27 18:10



Tôi cho một động thái mà 278 nên trở thành ngoài của HTTP Spec chính thức. - Chris Marisic
Không phải nó đã xử lý chúng một cách minh bạch? Nếu tài nguyên đã di chuyển, hãy xử lý nó một cách minh bạch nghĩa là lặp lại yêu cầu trên URL được cung cấp. Đó là những gì tôi mong đợi khi sử dụng API XMLHttpRequest. - Philippe Rathé
@ PhilippeRathé Đồng ý. Xử lý minh bạch chỉ là những gì tôi muốn. Và tôi không biết tại sao nó được coi là xấu. - smwikipedia
@smwikipedia Để sắp xếp chuyển hướng đánh dấu trong phần chính mà không cần chuyển hướng trang. - cmc


Giải pháp cuối cùng được triển khai là sử dụng trình bao bọc cho hàm gọi lại của cuộc gọi Ajax và trong kiểm tra trình bao bọc này cho sự tồn tại của một phần tử cụ thể trên đoạn HTML được trả về. Nếu phần tử đã được tìm thấy thì trình bao bọc thực thi một chuyển hướng. Nếu không, trình bao bọc sẽ chuyển tiếp cuộc gọi đến hàm gọi lại thực tế.

Ví dụ, hàm wrapper của chúng ta giống như sau:

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

Sau đó, khi thực hiện cuộc gọi Ajax, chúng tôi đã sử dụng một cái gì đó như:

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

Điều này làm việc cho chúng tôi vì tất cả các cuộc gọi Ajax luôn trả về HTML bên trong phần tử DIV mà chúng tôi sử dụng để thay thế một phần của trang. Ngoài ra, chúng tôi chỉ cần chuyển hướng đến trang đăng nhập.


85
2017-08-23 19:21



Lưu ý rằng điều này có thể được rút ngắn thành hàm cbWrapper (funct) {hàm trả về (dữ liệu) {if ($ ("# myForm", dữ liệu) .size ()> 0) top.location.href = "login"; khác funct (dữ liệu); }}. Sau đó bạn chỉ cần cbWrapper (myActualCB) khi gọi .post. Có, mã trong ý kiến ​​là một mớ hỗn độn nhưng cần lưu ý :) - Simen Echholt
kích thước được khấu hao để bạn có thể sử dụng .length ở đây thay cho kích thước - sunil
tôi đang cố gắng nó không làm việc cho tôi - Koi To Hoga


Tôi thích phương pháp của Timmerz với một chút chanh nhẹ. Nếu bạn quay trở lại contentType của text / html khi bạn đang mong đợi JSON, bạn có nhiều khả năng được chuyển hướng. Trong trường hợp của tôi, tôi chỉ cần tải lại trang và nó được chuyển hướng đến trang đăng nhập. Oh, và kiểm tra tình trạng jqXHR là 200, có vẻ ngớ ngẩn, bởi vì bạn đang ở trong chức năng lỗi, phải không? Nếu không, các trường hợp lỗi hợp lệ sẽ buộc tải lại lặp lại (oops)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});

57
2017-10-13 21:54



cảm ơn rất nhiều Brian, câu trả lời của bạn là tốt nhất cho kịch bản của tôi, mặc dù tôi muốn nếu có một kiểm tra an toàn hơn như so sánh url / trang nào đang chuyển hướng đến thay vì kiểm tra "kiểu nội dung" đơn giản. Tôi không thể tìm thấy trang nào đang chuyển hướng đến từ đối tượng jqXHR. - Johnny
Tôi đã kiểm tra trạng thái 401 và sau đó chuyển hướng. Làm việc như một nhà vô địch. - Eric


Sử dụng cấp thấp $.ajax() gọi điện:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

Hãy thử điều này để chuyển hướng:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

44
2018-05-23 10:02



Tôi đã cố gắng tránh những thứ cấp thấp. Dù sao, giả sử tôi sử dụng một cái gì đó giống như những gì bạn mô tả, làm thế nào để buộc một chuyển hướng trình duyệt khi tôi phát hiện ra rằng mã HTTP là 3xx? Mục tiêu của tôi là chuyển hướng người dùng, không chỉ để thông báo rằng phiên của anh ta / cô ấy đã hết hạn. - Elliot Vargas
Btw, $ .ajax () không phải là rất, rất thấp cấp. Nó chỉ ở mức độ thấp về jQuery vì có $ .get, $ .post, v.v ... đơn giản hơn nhiều so với $ .ajax và tất cả các tùy chọn của nó. - Till
Oh Boy! Xin lỗi tôi đã phải "không chấp nhận" câu trả lời của bạn, nó vẫn rất hữu ích. Thing là, các chuyển hướng được tự động quản lý bởi XMLHttpRequest, vì thế tôi luôn nhận được một mã trạng thái 200 sau khi chuyển hướng (tiếng thở dài!). Tôi nghĩ rằng tôi sẽ phải làm điều gì đó khó chịu như phân tích cú pháp HTML và tìm kiếm một điểm đánh dấu. - Elliot Vargas
Chỉ tò mò, nếu phiên kết thúc tại máy chủ, điều đó có nghĩa là máy chủ gửi SESSIONID khác không? chúng ta có thể phát hiện ra điều đó không? - Salamander2007
LƯU Ý: Điều này không làm việc cho chuyển hướng. ajax sẽ chuyển đến trang mới và trả lại mã trạng thái của nó.


Tôi chỉ muốn chia sẻ cách tiếp cận của tôi vì điều này có thể nó có thể giúp một người nào đó:

Về cơ bản tôi đã bao gồm một mô-đun JavaScript xử lý các công cụ xác thực như hiển thị tên người dùng và trường hợp này xử lý chuyển hướng đến trang đăng nhập.

Kịch bản của tôi: Về cơ bản chúng tôi có một máy chủ ISA ở giữa mà lắng nghe tất cả các yêu cầu và phản hồi với tiêu đề vị trí 302 và tiêu đề vị trí vào trang đăng nhập của chúng tôi.

Trong mô-đun JavaScript của tôi tiếp cận ban đầu giống như

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

Vấn đề (như nhiều người ở đây đã đề cập) là trình duyệt xử lý chuyển hướng bởi chính nó ajaxComplete gọi lại không bao giờ được gọi, nhưng thay vào đó tôi có phản hồi của trang Đăng nhập đã được chuyển hướng rõ ràng là một status 200. Vấn đề: làm thế nào để bạn phát hiện xem phản ứng thành công 200 là trang đăng nhập thực sự của bạn hay chỉ là một số trang tùy ý khác ??

Giải pháp

Vì tôi không thể chụp được 302 câu trả lời chuyển hướng, tôi đã thêm LoginPage trên trang đăng nhập của tôi có chứa url của trang đăng nhập. Trong mô-đun, bây giờ tôi nghe tiêu đề và thực hiện chuyển hướng:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

... và hoạt động như nét duyên dáng :). Bạn có thể thắc mắc tại sao tôi đưa url vào LoginPage tiêu đề ... về cơ bản vì tôi không tìm được cách xác định url của GET do chuyển hướng vị trí tự động từ xhr vật...


31
2018-05-06 23:55



+1 - nhưng tiêu đề tùy chỉnh bắt đầu bằng X-, do đó, tiêu đề tốt hơn để sử dụng sẽ là X-LoginPage: http://example.com/login. - uınbɐɥs
@ShaquinTrifonoff Không còn nữa. Tôi đã không sử dụng tiền tố X vì Tháng 6 năm 2011 một tài liệu ITEF đã đề xuất sự phản đối của họ và thực sự, với Tháng 6 năm 2012 không có chính thức rằng các tiêu đề tùy chỉnh sẽ không được thêm tiền tố X-. - Juri
Chúng tôi cũng có một máy chủ ISA và tôi chỉ chạy vào cùng một vấn đề. Thay vì làm việc xung quanh nó trong mã, chúng tôi đã sử dụng các hướng dẫn trong kb2596444 để cấu hình ISA để ngừng chuyển hướng. - scott stone


Tôi biết chủ đề này là cũ, nhưng tôi sẽ đưa ra một cách tiếp cận khác mà tôi đã tìm thấy và mô tả trước đây đây. Về cơ bản tôi đang sử dụng ASP.MVC với WIF (nhưng điều này không thực sự quan trọng đối với ngữ cảnh của chủ đề này - câu trả lời là đầy đủ cho dù khung được sử dụng. Đầu mối không thay đổi - xử lý các vấn đề liên quan đến lỗi xác thực trong khi thực hiện yêu cầu ajax).

Cách tiếp cận hiển thị bên dưới có thể được áp dụng cho tất cả các yêu cầu ajax ra khỏi hộp (nếu chúng không xác định lại trước sự kiện rõ ràng).

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

Trước khi bất kỳ yêu cầu ajax nào được thực hiện CheckPulse phương thức được gọi (phương thức điều khiển có thể đơn giản nhất):

[Authorize]
public virtual void CheckPulse() {}

Nếu người dùng không được xác thực (mã thông báo đã hết hạn) thì không thể truy cập phương thức này (được bảo vệ bởi Authorize thuộc tính). Vì khung công tác xử lý xác thực, trong khi mã thông báo hết hạn, nó sẽ đặt trạng thái http lên phản hồi. Nếu bạn không muốn trình duyệt xử lý 302 phản hồi một cách minh bạch, hãy xem nó trong Global.asax và thay đổi trạng thái phản hồi - ví dụ thành 200 OK. Ngoài ra, thêm tiêu đề, hướng dẫn bạn xử lý phản hồi như vậy theo cách đặc biệt (sau này ở phía máy khách):

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

Cuối cùng ở phía khách hàng kiểm tra cho tiêu đề tùy chỉnh như vậy. Nếu hiện tại - chuyển hướng đầy đủ đến trang đăng nhập nên được thực hiện (trong trường hợp của tôi window.location được thay thế bằng url từ yêu cầu được xử lý tự động bởi khung công tác của tôi).

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;
                }
            }
    });
}

26
2017-10-23 16:27



Tôi đã khắc phục sự cố bằng cách sử dụng sự kiện PostAuthenticateRequest thay vì sự kiện EndRequest. - Frinavale
@JaroslawWaliszko Tôi đã dán nhầm sự kiện vào câu trả lời cuối cùng của mình! Tôi có nghĩa là sự kiện PreSendRequestHeaders ..... không PostAuthenticateRequest! >> đỏ mặt << cảm ơn vì đã chỉ ra sai lầm của tôi. - Frinavale
@JaroslawWaliszko khi sử dụng WIF, bạn cũng có thể trả về 401 câu trả lời cho các yêu cầu AJAX và để javascript của bạn xử lý chúng. Ngoài ra, bạn giả định rằng tất cả 302 yêu cầu xác thực có thể không đúng trong mọi trường hợp. Tôi đã thêm một câu trả lời nếu có ai quan tâm. - Rob


Tôi nghĩ cách tốt hơn để xử lý việc này là tận dụng các mã phản hồi giao thức HTTP hiện có, cụ thể 401 Unauthorized.

Đây là cách tôi giải quyết nó:

  1. Phía máy chủ: Nếu phiên hết hạn và yêu cầu là ajax. gửi tiêu đề mã phản hồi 401
  2. Phía máy khách: Liên kết với sự kiện 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 này là chung chung hơn và bạn không viết một số spec / tiêu đề tùy chỉnh mới. Bạn cũng không cần phải sửa đổi bất kỳ cuộc gọi ajax hiện có nào.

Chỉnh sửa: Nhận xét của Per @ Rob bên dưới, 401 (mã trạng thái HTTP cho các lỗi xác thực) phải là chỉ báo. Xem 403 Forbidden vs 401 Phản hồi HTTP không được ủy quyền để biết thêm chi tiết. Với điều đó đang được nói một số khuôn khổ web sử dụng 403 cho cả hai xác thực và ủy quyền lỗi - để thích ứng cho phù hợp. Cảm ơn Rob.


21
2018-05-01 21:42



Tôi sử dụng cách tiếp cận tương tự. Liệu jQuery thực sự gọi ajaxSuccess trên mã lỗi 403? Tôi nghĩ rằng chỉ phần ajaxError thực sự là cần thiết - Marius Balčytis


Một giải pháp khác mà tôi tìm thấy (đặc biệt hữu ích nếu bạn muốn thiết lập hành vi toàn cầu) là sử dụng $.ajaxsetup() phương pháp cùng với statusCode bất động sản. Như những người khác đã chỉ ra, không sử dụng mã trạng thái chuyển hướng (3xx), thay vào đó hãy sử dụng 4xx mã trạng thái và xử lý phía máy khách chuyển hướng.

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

Thay thế 400 với mã trạng thái bạn muốn xử lý. Như đã đề cập 401 Unauthorized có thể là một ý tưởng hay. tôi sử dụng 400 vì nó rất không xác định và tôi có thể sử dụng 401 cho các trường hợp cụ thể hơn (như thông tin đăng nhập sai). Vì vậy, thay vì chuyển hướng trực tiếp chương trình phụ trợ của bạn nên trả về 4xx mã lỗi khi phiên hết hạn và bạn xử lý phía máy khách chuyển hướng. Hoạt động hoàn hảo cho tôi ngay cả với các khung công tác như backbone.js


17
2017-10-27 13:31



Đề cập đến chức năng trên trang? - Vikrant
@Vikrant Nếu tôi hiểu câu hỏi của bạn một cách chính xác, bạn có thể gọi hàm ngay sau khi jQuery được tải và trước khi bạn thực hiện các yêu cầu thực tế của mình. - morten.c