เพิ่ม "hook" ให้กับคำขอ AJAX ทั้งหมดบนเพจ


104

ฉันต้องการทราบว่าเป็นไปได้หรือไม่ที่จะ "เชื่อมโยง" กับคำขอ AJAX ทุกรายการ (ไม่ว่าจะเป็นในขณะที่กำลังจะถูกส่งหรือในเหตุการณ์) และดำเนินการ ณ จุดนี้ฉันสมมติว่ามีสคริปต์ของบุคคลที่สามอื่น ๆ ในหน้านี้ บางส่วนอาจใช้ jQuery ในขณะที่บางส่วนไม่ใช้ เป็นไปได้หรือไม่


เป็นไปได้ด้วย jQuery ดังนั้นจึงเป็นไปได้ด้วยจาวาสคริปต์แบบเก่า ๆ แต่คุณจะต้องมี "ตะขอ" อย่างน้อย 2 ตัวสำหรับแต่ละอัน อย่างไรก็ตามทำไมใช้ทั้งสองอย่างในหน้าเดียวกัน
yoda

2
วิธีการใช้ห้องสมุดนี้ github.com/slorber/ajax-interceptor
Sebastien Lorber

นี่เป็นทางออกที่ดี: stackoverflow.com/questions/25335648/…
phazei

2
หมายเหตุ: คำตอบสำหรับคำถามนี้ไม่ครอบคลุมถึงการโทรของ Ajax ที่ทำจากfetch()API รุ่นใหม่ในเบราว์เซอร์สมัยใหม่
jfriend00

1
@
n Nirvana

คำตอบ:


110

แรงบันดาลใจจากคำตอบของ avivฉันได้ตรวจสอบเล็กน้อยและนี่คือสิ่งที่ฉันคิดขึ้นมา
ฉันไม่แน่ใจว่ามันมีประโยชน์ทั้งหมดตามความคิดเห็นในสคริปต์และแน่นอนว่าจะใช้ได้กับเบราว์เซอร์ที่ใช้วัตถุ XMLHttpRequest ดั้งเดิมเท่านั้น
ฉันคิดว่ามันจะใช้งานได้ถ้ามีการใช้ไลบรารี javascript เนื่องจากจะใช้อ็อบเจ็กต์ดั้งเดิมถ้าเป็นไปได้

function addXMLRequestCallback(callback){
    var oldSend, i;
    if( XMLHttpRequest.callbacks ) {
        // we've already overridden send() so just add the callback
        XMLHttpRequest.callbacks.push( callback );
    } else {
        // create a callback queue
        XMLHttpRequest.callbacks = [callback];
        // store the native send()
        oldSend = XMLHttpRequest.prototype.send;
        // override the native send()
        XMLHttpRequest.prototype.send = function(){
            // process the callback queue
            // the xhr instance is passed into each callback but seems pretty useless
            // you can't tell what its destination is or call abort() without an error
            // so only really good for logging that a request has happened
            // I could be wrong, I hope so...
            // EDIT: I suppose you could override the onreadystatechange handler though
            for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                XMLHttpRequest.callbacks[i]( this );
            }
            // call the native send()
            oldSend.apply(this, arguments);
        }
    }
}

// e.g.
addXMLRequestCallback( function( xhr ) {
    console.log( xhr.responseText ); // (an empty string)
});
addXMLRequestCallback( function( xhr ) {
    console.dir( xhr ); // have a look if there is anything useful here
});

มันจะเป็นการดีที่จะขยายคำตอบนี้เพื่อรองรับการขอโพสต์
Sebastien Lorber

1
จากการใช้งานของคุณฉันได้เผยแพร่บางสิ่งบน NPM ที่ใช้ได้กับทั้งคำขอและการตอบกลับ! github.com/slorber/ajax-interceptor
Sebastien Lorber

4
console.log (xhr.responseText) เป็นสตริงว่างเนื่องจากในขณะนั้น xhr ว่างเปล่า หากคุณส่งตัวแปร xhr ไปยังตัวแปรส่วนกลางและตั้งค่าความล่าช้าไม่กี่วินาทีคุณจะสามารถเข้าถึงคุณสมบัติได้โดยตรง
Toolkit

5
คำตอบที่ดี หากคุณต้องการรับข้อมูลการตอบกลับให้ตรวจสอบ readyState และสถานะเมื่อสถานะเปลี่ยน xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log (JSON.parse (xhr.responseText)); }}
Stanley Wang

1
@ucheng ถ้าเราเพิ่ม onreadystatechange ของ xhr มันจะแทนที่วิธีการเปลี่ยนสถานะพร้อมเดิมได้ดีกว่าหรือไม่ให้ใช้ xhrObj.addEventListener ("load", fn)
Mohamed Hussain

127

หมายเหตุ: คำตอบที่ยอมรับไม่ได้ให้การตอบสนองที่แท้จริงเนื่องจากถูกเรียกเร็วเกินไป

คุณสามารถทำสิ่งนี้ได้ซึ่งจะดักฟังAJAX ใด ๆทั่วโลกโดยทั่วไปและไม่ทำให้การเรียกกลับใด ๆ ฯลฯ ที่อาจได้รับมอบหมายจากไลบรารี AJAX ของบุคคลที่สาม

(function() {
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        console.log('request started!');
        this.addEventListener('load', function() {
            console.log('request completed!');
            console.log(this.readyState); //will always be 4 (ajax is completed successfully)
            console.log(this.responseText); //whatever the response was
        });
        origOpen.apply(this, arguments);
    };
})();

เอกสารเพิ่มเติมเกี่ยวกับสิ่งที่คุณสามารถทำได้ที่นี่ด้วย addEventListener API ที่นี่:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress

(โปรดทราบว่าสิ่งนี้ใช้ไม่ได้ <= IE8)


ขอบคุณ! นั่นทำงานได้อย่างสมบูรณ์แบบ ฉันแก้ไขเพียงเล็กน้อย: this.statusคืนสถานะเซิร์ฟเวอร์ในกรณีนี้200หากคำขอนั้นตกลง 404 หากไม่พบองค์ประกอบ 500 ฯลฯ แต่รหัสทำงานได้อย่างสมบูรณ์
MrMins

1
สิ่งนี้อาจทำให้คุณรู้สึกแก่ แต่คุณสมควรได้รับความรักทั้งหมดนี้ ฉันติดอยู่ลึก ๆ ขอบคุณมาก
Carles Alcolea

เพียงเพราะฉันค้นหาสิ่งนี้มาเล็กน้อย การ"load"จัดงานเรียกร้องให้ประสบความสำเร็จเท่านั้น หากคุณไม่สนใจผลลัพธ์ (เพียงแค่การค้นหาสิ้นสุดลง) คุณสามารถใช้"loadend"เหตุการณ์ได้
Romuald Brunet

ใช้เวลาหลายวันในการค้นหาสิ่งนี้ ขอบคุณสำหรับอัจฉริยะชิ้นนี้
เดวิด

ฉันได้ลองคำตอบมากมายแล้วและใช้ได้กับ WKWebView ฉันมีปัญหาในการใช้เหตุการณ์ Ajax เนื่องจาก jQuery อาจไม่สามารถโหลดได้ก่อนที่ฉันจะเรียกใช้ evalJavascript หรือใช้ addUserScript ขอบคุณ!
Jake Cheng

21

เมื่อคุณพูดถึง jQuery ฉันรู้ jQuery ข้อเสนอ.ajaxSetup()วิธีการที่ชุดตัวเลือกอาแจ็กซ์ทั่วโลกที่มีการเรียกเหตุการณ์เช่นsuccess, errorและbeforeSend- ซึ่งเป็นสิ่งที่ดูเหมือนจะเป็นสิ่งที่คุณกำลังมองหา

$.ajaxSetup({
    beforeSend: function() {
        //do stuff before request fires
    }
});

แน่นอนคุณจะต้องตรวจสอบความพร้อมใช้งานของ jQuery ในหน้าใด ๆ ที่คุณพยายามใช้โซลูชันนี้


1
ขอบคุณสำหรับคำแนะนำ แต่น่าเสียดายที่สิ่งนี้ไม่ขัดขวางการโทรของ AJAX ที่ไม่ได้ใช้ AJAX
Yuliy

8
ในข่าววันนี้: ยูนิคอร์นที่ถูกฆ่าโดยการโทรของ AJAX ที่ไม่ได้ใช้ AJAX
Dashu

3
เขาหมายถึงวัตถุ jquery AJAX แต่ถึงอย่างนั้นคุณจะใช้อินสแตนซ์ jquery เดียวกันทุกครั้ง
Shishir Arora

1
เป็นประโยชน์อย่างมาก ต้องการเพิ่มทริกเกอร์อื่นคือ statusCode โดยการเสียบบางอย่างเช่น statusCode: {403: function () {error msg} คุณสามารถให้ global auth / perms / role ตรวจสอบกลับไปยังฟังก์ชัน ajax ทั้งหมดได้โดยไม่ต้องเขียนคำขอ. Ajax ซ้ำ
Watercayman

8

มีเคล็ดลับในการทำ

ก่อนที่สคริปต์ทั้งหมดจะทำงานให้ใช้อ็อบเจ็กต์ XHMHttpReuqest ดั้งเดิมและบันทึกในตัวแปรอื่น จากนั้นแทนที่ XMLHttpRequest ดั้งเดิมและกำหนดเส้นทางการเรียกทั้งหมดไปยังวัตถุของคุณ

รหัส Psuedo:

 var savd = XMLHttpRequest;
 XMLHttpRequest.prototype = function() {
         this.init = function() {
         }; // your code
         etc' etc'
 };

10
คำตอบนี้ไม่ถูกต้องนักหากคุณเปลี่ยนต้นแบบของวัตถุแม้แต่วัตถุที่บันทึกไว้ก็จะเปลี่ยนไป นอกจากนี้ต้นแบบทั้งหมดจะถูกแทนที่ด้วยฟังก์ชันเดียวซึ่งจะทำลายคำขอของ ajax ทั้งหมด มันเป็นแรงบันดาลใจให้ฉันเสนอคำตอบว่า
meouw

8

ฉันพบไลบรารีที่ดีใน Github ซึ่งทำงานได้ดีคุณต้องรวมไว้ก่อนไฟล์ js อื่น ๆ

https://github.com/jpillora/xhook

นี่คือตัวอย่างที่เพิ่มส่วนหัว http ให้กับการตอบกลับที่เข้ามา

xhook.after(function(request, response) {
  response.headers['Foo'] = 'Bar';
});

2
เยี่ยมมาก! แต่ไม่ทำงานกับแอปพลิเคชัน Cordova แม้จะมีปลั๊กอินมุมมองเว็บ Chrome ล่าสุดทางม้าลาย!
Islam Attrash

1
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับความต้องการเฉพาะของฉัน: ใน Wordpress การกรองกิจกรรมจากปลั๊กอิน Events Organizer แบบเต็มปฏิทิน
Alfredo Yong

ไม่ได้ผลสำหรับคำขอทั้งหมดการดึงข้อมูลและ xhr ตกลง แต่ตัวอย่างเช่นมันไม่จับ xhr จาก google recaptcha
Sergio Quintero

@SergioQuintero: ผมถือว่านี่เป็นปัญหาของคุณ GitHub: github.com/jpillora/xhook/issues/102 ลองลบความคิดเห็นของคุณที่นี่เนื่องจากไม่ใช่ปัญหากับห้องสมุด
thirtydot

@SergioQuintero การแทนที่อินเทอร์เฟซ XHR ใด ๆ จะเกิดขึ้นในเอกสารนั้นเท่านั้นไม่ใช่ในเบราว์เซอร์ทั่วโลกเพราะนั่นจะเป็นช่องโหว่ด้านความปลอดภัย / ความเป็นส่วนตัวขนาดใหญ่ reCAPTCHA ทำงานใน iframe และคุณไม่สามารถเรียกใช้การแทนที่ในนั้นได้
Walf

5

การใช้คำตอบของ "meouw" ฉันขอแนะนำให้ใช้วิธีแก้ไขปัญหาต่อไปนี้หากคุณต้องการเห็นผลลัพธ์ของคำขอ

function addXMLRequestCallback(callback) {
    var oldSend, i;
    if( XMLHttpRequest.callbacks ) {
        // we've already overridden send() so just add the callback
        XMLHttpRequest.callbacks.push( callback );
    } else {
        // create a callback queue
        XMLHttpRequest.callbacks = [callback];
        // store the native send()
        oldSend = XMLHttpRequest.prototype.send;
        // override the native send()
        XMLHttpRequest.prototype.send = function() {
            // call the native send()
            oldSend.apply(this, arguments);

            this.onreadystatechange = function ( progress ) {
               for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                    XMLHttpRequest.callbacks[i]( progress );
                }
            };       
        }
    }
}

addXMLRequestCallback( function( progress ) {
    if (typeof progress.srcElement.responseText != 'undefined' &&                        progress.srcElement.responseText != '') {
        console.log( progress.srcElement.responseText.length );
    }
});

3

jquery ...

<script>
   $(document).ajaxSuccess(
        function(event, xhr, settings){ 
          alert(xhr.responseText);
        }
   );
</script>

1
jQuery จะไม่จับคำขอที่สร้างขึ้นโดยใช้ไลบรารีอื่น ตัวอย่างเช่น ExtJS หากคุณใช้ jQuery เพียงอย่างเดียวมันเป็นคำตอบที่ดี มิฉะนั้นจะไม่ทำงานตลอดเวลา
npiani

1
มันจะไม่จับคำขอ jquery ด้วยซ้ำหากอินสแตนซ์ jquery แตกต่างกัน เปลี่ยน Protype ถ้าจำเป็น
Shishir Arora

3

นอกจากคำตอบของ meouw แล้วฉันต้องฉีดโค้ดลงใน iframe ที่ดักฟังการโทร XHR และใช้คำตอบข้างต้น อย่างไรก็ตามฉันต้องเปลี่ยน

XMLHttpRequest.prototype.send = function(){

ถึง:

XMLHttpRequest.prototype.send = function(body)

และฉันต้องเปลี่ยน

oldSend.apply(this, arguments);

ถึง:

oldSend.call(this, body);

นี้เป็นสิ่งจำเป็นที่จะได้รับมันทำงานใน IE9 กับโหมดเอกสาร IE8 หากไม่มีการปรับเปลี่ยนนี้การเรียกกลับบางอย่างที่สร้างโดยกรอบงานคอมโพเนนต์ (Visual WebGUI) ไม่ทำงาน ข้อมูลเพิ่มเติมที่ลิงค์เหล่านี้:

หากไม่มีการแก้ไขการโพสต์แบ็ค AJAX เหล่านี้ไม่ได้ยุติลง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.