การเข้าถึง 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
วิธีการทำงาน:
- เพิ่มบริการเวิร์กเกอร์ในเว็บไซต์ของคุณ
- ดูทุกคำขอที่ถูกส่งออกไป
- ทำให้ service worker ใช้
fetchคำขอด้วยฟังก์ชันrespondWith - เมื่อการตอบกลับมาถึง ให้อ่านข้อความหัวข้อ
- ส่งเฮดเดอร์จากบริการเวิร์กเกอร์ไปยังหน้าโดยใช้ฟังก์ชัน
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"]
