เพิ่ม "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.