ค้นหาเว็บไซต์

การเข้าถึง HTTP Headers ของหน้าเว็บใน JavaScript


ฉันจะเข้าถึงส่วนหัวการตอบกลับ HTTP ของหน้าเพจผ่าน JavaScript ได้อย่างไร?

เกี่ยวกับ คำถามนี้ ซึ่งถูกแก้ไขเพื่อถามเกี่ยวกับการเข้าถึงเฮดเดอร์ HTTP สองตัวเฉพาะ

ที่เกี่ยวข้อง:
ฉันจะเข้าถึงฟิลด์หัวข้อคำขอ HTTP ผ่าน JavaScript ได้อย่างไร?

คำตอบ #1

ไม่สามารถอ่านเฮดเดอร์ปัจจุบันได้ คุณสามารถส่งคำขออีกครั้งไปยัง URL เดียวกันและอ่านเฮดเดอร์ของมันได้ แต่ไม่สามารถรับประกันได้ว่าเฮดเดอร์จะเหมือนกับปัจจุบันแน่นอน


ใช้โค้ด JavaScript ต่อไปนี้เพื่อดึง HTTP headers ทั้งหมดโดยการทำคำขอ get:

var req = new XMLHttpRequest();
req.open('GET', document.location, true);
req.send(null);
req.onload = function() {
  var headers = req.getAllResponseHeaders().toLowerCase();
  console.log(headers);
};

คำตอบ #2

น่าเสียดายที่ไม่มี API ที่จะให้คุณได้รับหัวข้อการตอบกลับ HTTP สำหรับคำขอหน้าแรกของคุณ นั่นคือคำถามต้นฉบับที่โพสต์ที่นี่ และก็มีการถามซ้ำหลายครั้งด้วย เพราะบางคนอยากได้หัวข้อการตอบกลับจริง ๆ ของคำขอหน้าแรกโดยไม่ต้องส่งคำขออีกครั้ง

สำหรับคำขอ AJAX:

หากมีการส่งคำขอ HTTP ผ่าน AJAX เป็นไปได้ที่จะได้รับส่วนหัวการตอบสนองด้วยวิธี getAllResponseHeaders() ซึ่งเป็นส่วนหนึ่งของ API ของ XMLHttpRequest เพื่อดูว่าสิ่งนี้สามารถนำไปใช้ได้อย่างไร ให้ตรวจสอบฟังก์ชัน fetchSimilarHeaders() ด้านล่าง โปรดทราบว่านี่เป็นวิธีแก้ปัญหาชั่วคราวที่อาจไม่เชื่อถือได้สำหรับบางแอปพลิเคชัน

myXMLHttpRequest.getAllResponseHeaders();
  • API ได้ถูกระบุในข้อเสนอแนะผู้สมัครดังต่อไปนี้สำหรับ XMLHttpRequest: XMLHttpRequest - W3C Candidate Recommendation 3 สิงหาคม 2010

  • โดยเฉพาะ วิธี getAllResponseHeaders() ถูกระบุไว้ในส่วนต่อไปนี้: w3.org: XMLHttpRequest: วิธี getallresponseheaders()

  • เอกสารของ MDN ก็ดีเช่นกัน: developer.mozilla.org: XMLHttpRequest.

นี่จะไม่ให้ข้อมูลเกี่ยวกับหัวข้อการตอบสนอง HTTP ของคำขอหน้าเดิม แต่สามารถใช้เพื่อสร้างการคาดเดาอย่างมีเหตุผลเกี่ยวกับหัวข้อเหล่านั้นได้ ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนั้นจะอธิบายในส่วนถัดไป

การดึงค่าหัวข้อจากคำขอหน้าเริ่มต้น:

คำถามนี้ถูกถามครั้งแรกเมื่อหลายปีก่อน โดยถามโดยเฉพาะเกี่ยวกับวิธีการเข้าถึง HTTP response headers ต้นฉบับสำหรับ หน้าปัจจุบัน (เช่น หน้าที่เดียวกันที่รันจาวาสคริปต์อยู่) ซึ่งนี่เป็นคำถามที่ต่างออกไปจากการเพียงแค่ดึง response headers ของ HTTP request ใด ๆ สำหรับคำขอหน้าเริ่มต้น headers จะไม่สามารถเข้าถึงได้โดยตรงจากจาวาสคริปต์ ว่าค่าของ header ที่คุณต้องการจะมีความสม่ำเสมอและเพียงพอหากคุณขอหน้าดังกล่าวอีกครั้งผ่าน AJAX นั้นจะขึ้นอยู่กับแอปพลิเคชันเฉพาะของคุณ

ต่อไปนี้คือข้อเสนอแนะบางประการในการแก้ปัญหานั้น

1. คำขอเกี่ยวกับทรัพยากรซึ่งค่อนข้างคงที่

หากการตอบสนองมีความคงที่เป็นส่วนใหญ่และไม่คาดว่าหัวข้อข้อมูลจะเปลี่ยนแปลงมากระหว่างคำร้องขอ คุณสามารถทำคำร้อง AJAX สำหรับหน้าที่คุณอยู่ในปัจจุบันและสมมติว่าพวกมันมีค่าเดียวกันกับที่เป็นส่วนหนึ่งของการตอบสนอง HTTP ของหน้า ซึ่งสิ่งนี้อาจช่วยให้คุณเข้าถึงหัวข้อข้อมูลที่คุณต้องการโดยใช้ API XMLHttpRequest ที่ดีตามที่อธิบายไว้ข้างต้น

function fetchSimilarHeaders (callback) {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function () {
        if (request.readyState === XMLHttpRequest.DONE) {
            //
            // The following headers may often be similar
            // to those of the original page request...
            //
            if (callback && typeof callback === 'function') {
                callback(request.getAllResponseHeaders());
            }
        }
    };

    //
    // Re-request the same page (document.location)
    // We hope to get the same or similar response headers to those which 
    // came with the current page, but we have no guarantee.
    // Since we are only after the headers, a HEAD request may be sufficient.
    //
    request.open('HEAD', document.location, true);
    request.send(null);
}

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

2. การสรุปข้อสันนิษฐาน

มี คุณสมบัติบางอย่างของ BOM (Browser Object Model) ที่เบราว์เซอร์กำหนดโดยการดูที่เฮดเดอร์ คุณสมบัติบางอย่างเหล่านี้สะท้อนเฮดเดอร์ HTTP โดยตรง (เช่น navigator.userAgent ถูกตั้งค่าเป็นค่าของฟิลด์เฮดเดอร์ HTTP User-Agent) โดยการสำรวจคุณสมบัติที่มีอยู่ คุณอาจสามารถหาสิ่งที่คุณต้องการ หรือหาสัญญาณบางอย่างเพื่อบ่งชี้ว่าสิ่งใดอยู่ใน HTTP response

3. ซ่อนไว้

ถ้าคุณควบคุมฝั่งเซิร์ฟเวอร์ได้ คุณสามารถเข้าถึงหัวข้อใดก็ได้ตามที่คุณต้องการเมื่อสร้างการตอบสนองเต็มรูปแบบ ค่าเหล่านี้สามารถส่งไปยังไคลเอนต์พร้อมกับหน้า, ซ่อนไว้ในมาร์กอัปบางอย่าง หรืออาจอยู่ในโครงสร้าง JSON แบบฝัง หากคุณต้องการให้ทุกหัวข้อ HTTP request พร้อมใช้งานสำหรับจาวาสคริปต์ของคุณ คุณสามารถวนลูปผ่านมันบนเซิร์ฟเวอร์และส่งกลับเป็นค่าที่ซ่อนอยู่ในมาร์กอัป มันอาจไม่เหมาะสมที่จะส่งค่าหัวข้อในลักษณะนี้ แต่คุณก็สามารถทำได้แน่นอนสำหรับค่าที่คุณต้องการ วิธีแก้ปัญหานี้อาจไม่ค่อยมีประสิทธิภาพเช่นกัน แต่ก็สามารถทำงานได้หากคุณต้องการ

คำตอบ #3

(2024) คำตอบ โดยไม่ต้อง เรียก HTTP เพิ่มเติม (ใช้งานได้ทุกที่ยกเว้น Safari)

แม้ว่าจะไม่สามารถ โดยทั่วไป อ่านหัวข้อการตอบสนอง HTTP แบบสุ่มของการนำทาง HTML ระดับบนสุดได้ แต่ถ้าคุณควบคุมเซิร์ฟเวอร์ (หรืออุปกรณ์กลางบนเส้นทาง) และต้องการเปิดเผยข้อมูลบางอย่างให้กับ JavaScript ซึ่งไม่สามารถเปิดเผยได้ง่ายโดยวิธีอื่นนอกจากผ่านหัวข้อ:

คุณสามารถใช้ส่วนหัว Server-Timing เพื่อเปิดเผยข้อมูลคู่คีย์-ค่าแบบสุ่ม และมันจะสามารถอ่านได้โดย JavaScript

(*ในเบราว์เซอร์ที่รองรับ: Firefox 61, Chrome 65, Edge 79; ไม่มี IE, ยังไม่มี Safari และยังไม่มีแผนการเปิดตัวในทันที ณ เดือนกันยายน 2021; แม้ว่ามันอาจถูกใช้งานสำหรับ JS ที่มีต้นทางเดียวกันในอนาคต)

ตัวอย่าง :

server-timing: key;desc="value"
  • คุณสามารถใช้หัวข้อนี้หลายครั้งสำหรับข้อมูลหลายรายการ:
server-timing: key1;desc="value1"
server-timing: key2;desc="value2"
  • หรือใช้รุ่นขนาดกะทัดรัดของมันที่คุณสามารถเปิดเผยข้อมูลหลายชิ้นในส่วนหัวเดียว โดยคั่นด้วยเครื่องหมายจุลภาค
server-timing: key1;desc="value1", key2;desc="value2"

ตัวอย่างวิธีที่วิกิพีเดียใช้หัวข้อนี้เพื่อแสดงข้อมูลเกี่ยวกับการเข้าใช้งานแคชที่สำเร็จ/ไม่สำเร็จ:

ตัวอย่างโค้ด (ต้องพิจารณาการขาดการรองรับเบราว์เซอร์ใน Safari และ IE):

if (window.performance && performance.getEntriesByType) { // avoid error in Safari 10, IE9- and other old browsers
    let navTiming = performance.getEntriesByType('navigation')
    if (navTiming.length > 0) { // still not supported as of Safari 14...
        let serverTiming = navTiming[0].serverTiming
        if (serverTiming && serverTiming.length > 0) {
            for (let i=0; i<serverTiming.length; i++) {
                console.log(`${serverTiming[i].name} = ${serverTiming[i].description}`)
            }
        }
    }
}

นี่จะบันทึก cache=hit-front ในเบราว์เซอร์ที่รองรับ

บันทึก:

  • ตามที่กล่าวไว้บน MDN API จะรองรับเฉพาะผ่าน HTTPS เท่านั้น
  • ถ้า JS ของคุณถูกให้บริการจากโดเมนอื่น คุณต้องเพิ่มหัวข้อการตอบสนอง Timing-Allow-Origin เพื่อให้ข้อมูลสามารถอ่านได้โดย JS (Timing-Allow-Origin: * หรือ Timing-Allow-Origin: https://www.example.com)
  • หัวข้อ Server-Timing ยังรองรับฟิลด์ dur (header) ซึ่งสามารถอ่านเป็น duration ในฝั่ง JS ได้ แต่เป็นทางเลือกและตั้งค่าเริ่มต้นเป็น 0 ใน JS หากไม่ได้ส่งมา
  • เกี่ยวกับการสนับสนุน Safari: ดูบั๊ก 1 และ บั๊ก 2 และ บั๊ก 3
  • คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเวลาเซิร์ฟเวอร์ในโพสต์บล็อกนี้
  • โปรดทราบว่า buffer ของรายการประสิทธิภาพอาจถูกล้างโดย JS บนหน้าเว็บ (ผ่านการเรียก API) หรือโดยเบราว์เซอร์ หากหน้านั้นส่งคำขอไปยังทรัพยากรย่อยมากเกินไป ด้วยเหตุนี้ คุณควรบันทึกข้อมูลโดยเร็วที่สุด และ/หรือใช้ API PerformanceObserver แทน ดูรายละเอียดเพิ่มเติมได้จากโพสต์บล็อก

คำตอบ #4

การใช้ XmlHttpRequest คุณสามารถดึงหน้าปัจจุบันขึ้นมาแล้วตรวจสอบหัวข้อ http ของการตอบสนองได้

กรณีที่ดีที่สุดคือต้องทำการขอ HEAD แล้วจึงตรวจสอบเฮดเดอร์

สำหรับตัวอย่างบางส่วนของการทำสิ่งนี้ ลองดูที่ http://www.jibbering.com/2002/4/httprequest.html

แค่ความคิดเห็นเล็กๆ ของฉัน

คำตอบ #5

โซลูชันที่มี Service Workers

พนักงานบริการสามารถเข้าถึงข้อมูลเครือข่ายได้ ซึ่งรวมถึงส่วนหัว ข้อดีคือมันทำงานได้กับคำขอทุกประเภท ไม่ใช่เพียงแค่ XMLHttpRequest

วิธีการทำงาน:

  1. เพิ่มบริการเวิร์กเกอร์ในเว็บไซต์ของคุณ
  2. ดูทุกคำขอที่ถูกส่งออกไป
  3. ทำให้ service worker ใช้ fetch คำขอด้วยฟังก์ชัน respondWith
  4. เมื่อการตอบกลับมาถึง ให้อ่านข้อความหัวข้อ
  5. ส่งเฮดเดอร์จากบริการเวิร์กเกอร์ไปยังหน้าโดยใช้ฟังก์ชัน postMessage

ตัวอย่างที่ใช้ได้จริง:

การทำงานของ service workers เป็นเรื่องซับซ้อนเล็กน้อยในการเข้าใจ ดังนั้นผมจึงสร้างไลบรารีเล็กๆ ที่ทำทั้งหมดนี้ให้ มันสามารถใช้ได้บน github: https://github.com/gmetais/sw-get-headers.

ข้อจำกัด:

  • เว็บไซต์จำเป็นต้องใช้ HTTPS
  • เบราว์เซอร์ต้องรองรับ API ของ Service Workers
  • นโยบายแบบโดเมนเดียว/ข้ามโดเมนกำลังทำงาน เหมือนกับบน XMLHttpRequest

คำตอบ #6

วิธีอื่นในการส่งข้อมูลเฮดเดอร์ไปยัง JavaScript คือผ่านคุกกี้ เซิร์ฟเวอร์สามารถดึงข้อมูลใด ๆ ที่ต้องการจากเฮดเดอร์ของคำขอและส่งกลับไปในเฮดเดอร์ตอบสนอง Set-Cookie — และสามารถอ่านคุกกี้ใน JavaScript ได้ อย่างไรก็ตาม ตามที่ keparo กล่าว สิ่งที่ดีที่สุดคือต้องทำเพียงหนึ่งหรือสองเฮดเดอร์เท่านั้น แทนที่จะทำทั้งหมด

คำตอบ #7

สำหรับผู้ที่กำลังมองหาวิธีในการแยกวิเคราะห์หัวข้อ HTTP ทั้งหมดเป็นอ็อบเจ็กต์ที่สามารถเข้าถึงได้เหมือนพจนานุกรม headers["content-type"] ผมได้สร้างฟังก์ชัน parseHttpHeaders:

function parseHttpHeaders(httpHeaders) {
    return httpHeaders.split("\n")
     .map(x=>x.split(/: */,2))
     .filter(x=>x[0])
     .reduce((ac, x)=>{ac[x[0]] = x[1];return ac;}, {});
}

var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = parseHttpHeaders(req.getAllResponseHeaders());
// Now we can do:  headers["content-type"]

บทความที่เกี่ยวข้อง