Codelab นี้เป็นส่วนหนึ่งของหลักสูตรการฝึกอบรมการพัฒนา Progressive Web App ซึ่งพัฒนาโดยทีมฝึกอบรมของ Google Developers คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ
ดูรายละเอียดทั้งหมดเกี่ยวกับหลักสูตรได้ที่ภาพรวมการพัฒนา Progressive Web App
บทนำ
ใน Lab นี้ เราจะแนะนำวิธีใช้ Fetch API ซึ่งเป็นอินเทอร์เฟซที่เรียบง่ายสำหรับการดึงข้อมูลทรัพยากร และเป็น API ที่ได้รับการปรับปรุงจาก XMLHttpRequest API
สิ่งที่คุณจะได้เรียนรู้
- วิธีใช้ Fetch API เพื่อขอทรัพยากร
- วิธีส่งคำขอ GET, HEAD และ POST ด้วยการดึงข้อมูล
- วิธีอ่านและตั้งค่าส่วนหัวที่กำหนดเอง
- การใช้งานและข้อจำกัดของ CORS
สิ่งที่ควรทราบ
- JavaScript และ HTML พื้นฐาน
- คุ้นเคยกับแนวคิดและไวยากรณ์พื้นฐานของ Promise ใน ES2015
สิ่งที่คุณจะต้องมี
- คอมพิวเตอร์ที่มีสิทธิ์เข้าถึงเทอร์มินัล/เชลล์
- การเชื่อมต่ออินเทอร์เน็ต
- เบราว์เซอร์ที่รองรับ Fetch
- โปรแกรมแก้ไขข้อความ
- Node และ npm
หมายเหตุ: แม้ว่าปัจจุบันเบราว์เซอร์บางตัวจะไม่รองรับ Fetch API แต่ก็มี polyfill
ดาวน์โหลดหรือโคลนที่เก็บ pwa-training-labs จาก GitHub และติดตั้ง Node.js เวอร์ชัน LTS หากจำเป็น
เปิดบรรทัดคำสั่งของคอมพิวเตอร์ ไปที่ไดเรกทอรี fetch-api-lab/app/
แล้วเริ่มเซิร์ฟเวอร์การพัฒนาซอฟต์แวร์ภายใน
cd fetch-api-lab/app npm install node server.js
คุณสิ้นสุดการทำงานของเซิร์ฟเวอร์ได้ทุกเมื่อด้วย Ctrl-c
เปิดเบราว์เซอร์ แล้วไปที่ localhost:8081/
คุณควรเห็นหน้าเว็บที่มีปุ่มสำหรับส่งคำขอ (ปุ่มจะยังใช้งานไม่ได้)
หมายเหตุ: ยกเลิกการลงทะเบียน Service Worker และล้างแคชทั้งหมดของ Service Worker สำหรับ localhost เพื่อไม่ให้รบกวน Lab ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome คุณทำได้โดยคลิกล้างข้อมูลเว็บไซต์จากส่วนล้างพื้นที่เก็บข้อมูลของแท็บแอปพลิเคชัน
เปิดโฟลเดอร์ fetch-api-lab/app/
ในโปรแกรมแก้ไขข้อความที่ต้องการ app/
โฟลเดอร์คือที่ที่คุณจะสร้าง Lab
โฟลเดอร์นี้ประกอบด้วย
echo-servers/
มีไฟล์ที่ใช้สำหรับเรียกใช้เซิร์ฟเวอร์ทดสอบexamples/
มีทรัพยากรตัวอย่างที่เราใช้ในการทดลองกับ Fetchjs/main.js
คือ JavaScript หลักของแอป และเป็นที่ที่คุณจะเขียนโค้ดทั้งหมดindex.html
คือหน้า HTML หลักสำหรับเว็บไซต์/แอปพลิเคชันตัวอย่างpackage-lock.json
และpackage.json
เป็นไฟล์การกำหนดค่าสำหรับเซิร์ฟเวอร์การพัฒนาและการขึ้นต่อกันของเซิร์ฟเวอร์ Echoserver.js
เป็นเซิร์ฟเวอร์การพัฒนาโหนด
Fetch API มีอินเทอร์เฟซที่ค่อนข้างเรียบง่าย ส่วนนี้อธิบายวิธีเขียนคำขอ HTTP พื้นฐานโดยใช้ฟังก์ชัน Fetch
ดึงข้อมูลไฟล์ JSON
ใน js/main.js
ปุ่มดึงข้อมูล JSON ของแอปจะเชื่อมต่อกับฟังก์ชัน fetchJSON
อัปเดตฟังก์ชัน fetchJSON
เพื่อขอไฟล์ examples/animals.json
และบันทึกการตอบกลับโดยทำดังนี้
function fetchJSON() {
fetch('examples/animals.json')
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูล JSON คอนโซลควรบันทึกการตอบกลับของคำสั่งดึงข้อมูล
คำอธิบาย
fetch
เมธอดจะยอมรับเส้นทางของทรัพยากรที่เราต้องการดึงข้อมูลเป็นพารามิเตอร์ ในกรณีนี้คือ examples/animals.json
fetch
จะแสดงผล Promise ที่แปลงเป็นออบเจ็กต์ Response หาก Promise แก้ไขได้ ระบบจะส่งการตอบกลับไปยังฟังก์ชัน logResult
หาก Promise ปฏิเสธ catch
จะเข้ามาแทนที่และส่งต่อข้อผิดพลาดไปยังฟังก์ชัน logError
ออบเจ็กต์การตอบกลับแสดงถึงการตอบกลับคำขอ ซึ่งมีเนื้อหาการตอบกลับ รวมถึงพร็อพเพอร์ตี้และเมธอดที่มีประโยชน์
ทดสอบคำตอบที่ไม่ถูกต้อง
ตรวจสอบการตอบกลับที่บันทึกไว้ในคอนโซล จดค่าของพร็อพเพอร์ตี้ status
, url
และ ok
แทนที่ทรัพยากร examples/animals.json
ใน fetchJSON
ด้วย examples/non-existent.json
ตอนนี้ฟังก์ชัน fetchJSON
ที่อัปเดตแล้วควรมีลักษณะดังนี้
function fetchJSON() {
fetch('examples/non-existent.json')
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูล JSON อีกครั้งเพื่อลองดึงข้อมูลทรัพยากรที่ไม่มีอยู่
สังเกตว่าการดึงข้อมูลเสร็จสมบูรณ์และไม่ได้ทริกเกอร์catch
บล็อก ตอนนี้ให้ค้นหาพร็อพเพอร์ตี้ status
, URL
และ ok
ของคำตอบใหม่
ค่าในไฟล์ทั้ง 2 ควรแตกต่างกัน (คุณเข้าใจไหมว่าทำไม) หากคุณได้รับข้อผิดพลาดในคอนโซล ค่าตรงกับบริบทของข้อผิดพลาดหรือไม่
คำอธิบาย
เหตุใดการตอบกลับที่ไม่สำเร็จจึงไม่เปิดใช้งานcatch
บล็อก นี่เป็นหมายเหตุสำคัญสำหรับ Fetch และ Promise ซึ่งการตอบกลับที่ไม่ถูกต้อง (เช่น 404) จะยังคงแก้ไขได้ สัญญาการดึงข้อมูลจะปฏิเสธก็ต่อเมื่อคำขอไม่สามารถดำเนินการให้เสร็จสมบูรณ์ได้ ดังนั้นคุณต้องตรวจสอบความถูกต้องของการตอบกลับเสมอ เราจะตรวจสอบคำตอบในส่วนถัดไป
สำหรับข้อมูลเพิ่มเติม
ตรวจสอบความถูกต้องของคำตอบ
เราต้องอัปเดตรหัสเพื่อตรวจสอบความถูกต้องของการตอบกลับ
ใน main.js
ให้เพิ่มฟังก์ชันเพื่อตรวจสอบคำตอบ
function validateResponse(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
จากนั้นแทนที่ fetchJSON
ด้วยโค้ดต่อไปนี้
function fetchJSON() {
fetch('examples/non-existent.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูล JSON ตรวจสอบคอนโซล ตอนนี้คำตอบสำหรับ examples/non-existent.json
ควรทริกเกอร์บล็อก catch
แทนที่ examples/non-existent.json
ในฟังก์ชัน fetchJSON
ด้วย examples/animals.json
เดิม ตอนนี้ฟังก์ชันที่อัปเดตแล้วควรมีลักษณะดังนี้
function fetchJSON() {
fetch('examples/animals.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูล JSON คุณควรเห็นว่าระบบบันทึกการตอบกลับได้สำเร็จเช่นเดิม
คำอธิบาย
ตอนนี้เราได้เพิ่มการตรวจสอบ validateResponse
แล้ว คำตอบที่ไม่ดี (เช่น 404) จะทำให้เกิดข้อผิดพลาดและ catch
จะเข้ามาแทนที่ ซึ่งจะช่วยให้เราจัดการการตอบกลับที่ไม่สำเร็จและป้องกันไม่ให้การตอบกลับที่ไม่คาดคิดแพร่กระจายลงในห่วงโซ่การดึงข้อมูล
อ่านคำตอบ
การตอบกลับของ Fetch จะแสดงเป็น ReadableStreams (ข้อกำหนดของสตรีม) และต้องอ่านเพื่อเข้าถึงเนื้อหาของการตอบกลับ ออบเจ็กต์การตอบกลับมีเมธอดสำหรับดำเนินการนี้
ใน main.js
ให้เพิ่มฟังก์ชัน readResponseAsJSON
ด้วยโค้ดต่อไปนี้
function readResponseAsJSON(response) {
return response.json();
}
จากนั้นแทนที่ฟังก์ชัน fetchJSON
ด้วยโค้ดต่อไปนี้
function fetchJSON() {
fetch('examples/animals.json') // 1
.then(validateResponse) // 2
.then(readResponseAsJSON) // 3
.then(logResult) // 4
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูล JSON ตรวจสอบคอนโซลเพื่อให้แน่ใจว่าระบบบันทึก JSON จาก examples/animals.json
(แทนที่จะเป็นออบเจ็กต์การตอบกลับ)
คำอธิบาย
มาดูสิ่งที่เกิดขึ้นกัน
ขั้นตอนที่ 1 เรียกใช้ Fetch ในทรัพยากร examples/animals.json
Fetch จะแสดงผล Promise ที่เปลี่ยนเป็นออบเจ็กต์ Response เมื่อ Promise แก้ไขแล้ว ระบบจะส่งออบเจ็กต์การตอบกลับไปยัง validateResponse
ขั้นตอนที่ 2 validateResponse
ตรวจสอบว่าการตอบกลับถูกต้องหรือไม่ (เป็นการตอบกลับ 200 หรือไม่) หากไม่เป็นเช่นนั้น ระบบจะแสดงข้อผิดพลาด ข้ามบล็อก then
ที่เหลือ และทริกเกอร์บล็อก catch
ซึ่งถือเป็นสิ่งสำคัญอย่างยิ่ง หากไม่มีการตรวจสอบนี้ ระบบจะส่งคำตอบที่ไม่ดีไปยังเชนและอาจทำให้โค้ดในภายหลังที่อาจต้องอาศัยการรับคำตอบที่ถูกต้องใช้งานไม่ได้ หากการตอบกลับถูกต้อง ระบบจะส่งไปยัง readResponseAsJSON
ขั้นตอนที่ 3 readResponseAsJSON
อ่านเนื้อหาของการตอบกลับโดยใช้วิธี Response.json() เมธอดนี้จะแสดงผล Promise ที่แปลงเป็น JSON เมื่อคำสัญญานี้ได้รับการแก้ไข ระบบจะส่งข้อมูล JSON ไปยัง logResult
(หาก Promise จาก response.json()
ปฏิเสธ ระบบจะเรียกใช้บล็อก catch
)
ขั้นตอนที่ 4 สุดท้าย logResult
จะบันทึกข้อมูล JSON จากคำขอดั้งเดิมไปยัง examples/animals.json
สำหรับข้อมูลเพิ่มเติม
Fetch ไม่ได้จำกัดอยู่แค่ JSON ในตัวอย่างนี้ เราจะดึงข้อมูลรูปภาพและต่อท้ายในหน้าเว็บ
ใน main.js
ให้เขียนฟังก์ชัน showImage
ด้วยโค้ดต่อไปนี้
function showImage(responseAsBlob) {
const container = document.getElementById('img-container');
const imgElem = document.createElement('img');
container.appendChild(imgElem);
const imgUrl = URL.createObjectURL(responseAsBlob);
imgElem.src = imgUrl;
}
จากนั้นเพิ่มreadResponseAsBlob
ฟังก์ชันที่อ่านการตอบกลับเป็น Blob
function readResponseAsBlob(response) {
return response.blob();
}
อัปเดตฟังก์ชัน fetchImage
ด้วยโค้ดต่อไปนี้
function fetchImage() {
fetch('examples/fetching.jpg')
.then(validateResponse)
.then(readResponseAsBlob)
.then(showImage)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูลรูปภาพ คุณจะเห็นสุนัขน่ารักคาบไม้ในหน้าเว็บ (เป็นมุกตลกเกี่ยวกับคำว่า "คาบ")
คำอธิบาย
ในตัวอย่างนี้ ระบบกำลังดึงข้อมูลรูปภาพ examples/fetching.jpg
เช่นเดียวกับการออกกำลังกายครั้งก่อน ระบบจะตรวจสอบการตอบกลับด้วย validateResponse
จากนั้นจะอ่านการตอบกลับเป็น Blob (แทนที่จะเป็น JSON เหมือนในส่วนก่อนหน้า) ระบบจะสร้างองค์ประกอบรูปภาพและต่อท้ายในหน้าเว็บ และตั้งค่าแอตทริบิวต์ src
ของรูปภาพเป็น URL ข้อมูลที่แสดง Blob
หมายเหตุ: ระบบจะใช้เมธอด createObjectURL()
ของออบเจ็กต์ URL เพื่อสร้าง URL ของข้อมูลที่แสดง Blob โปรดทราบว่า คุณตั้งค่าแหล่งที่มาของรูปภาพเป็น Blob โดยตรงไม่ได้ ต้องแปลง Blob เป็น URL ข้อมูล
สำหรับข้อมูลเพิ่มเติม
ส่วนนี้เป็นความท้าทายที่ไม่บังคับ
อัปเดตฟังก์ชัน fetchText
เพื่อ
- ดึงข้อมูล
/examples/words.txt
- ตรวจสอบคำตอบด้วย
validateResponse
- อ่านการตอบกลับเป็นข้อความ (คำแนะนำ: ดู Response.text())
- และแสดงข้อความในหน้าเว็บ
คุณสามารถใช้ฟังก์ชัน showText
นี้เป็นตัวช่วยในการแสดงข้อความสุดท้ายได้
function showText(responseAsText) {
const message = document.getElementById('message');
message.textContent = responseAsText;
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกดึงข้อมูลข้อความ หากติดตั้งใช้งาน fetchText
อย่างถูกต้อง คุณควรเห็นข้อความที่เพิ่มในหน้า
หมายเหตุ: แม้ว่าการดึงข้อมูล HTML และการต่อท้ายโดยใช้แอตทริบิวต์ innerHTML
อาจเป็นสิ่งที่น่าสนใจ แต่โปรดระมัดระวัง ซึ่งอาจทำให้เว็บไซต์เสี่ยงต่อการโจมตีแบบ Cross-Site Scripting
สำหรับข้อมูลเพิ่มเติม
โดยค่าเริ่มต้น ฟีเจอร์ Fetch จะใช้เมธอด GET ซึ่งจะดึงข้อมูลทรัพยากรที่เฉพาะเจาะจง แต่ Fetch ยังใช้วิธีการ HTTP อื่นๆ ได้ด้วย
ส่งคำขอ HEAD
แทนที่ฟังก์ชัน headRequest
ด้วยโค้ดต่อไปนี้
function headRequest() {
fetch('examples/words.txt', {
method: 'HEAD'
})
.then(validateResponse)
.then(readResponseAsText)
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกคำขอ HEAD สังเกตว่าเนื้อหาข้อความที่บันทึกว่างเปล่า
คำอธิบาย
เมธอด fetch
สามารถรับพารามิเตอร์ที่ 2 ซึ่งเป็นพารามิเตอร์ที่ไม่บังคับ init
พารามิเตอร์นี้ช่วยให้กำหนดค่าคำขอเรียกข้อมูลได้ เช่น request method, โหมดแคช, ข้อมูลเข้าสู่ระบบ และอื่นๆ
ในตัวอย่างนี้ เราตั้งค่าเมธอดคำขอเรียกข้อมูลเป็น HEAD โดยใช้พารามิเตอร์ init
คำขอ HEAD จะเหมือนกับคำขอ GET ยกเว้นว่าเนื้อหาของการตอบกลับจะว่างเปล่า คุณสามารถใช้คำขอลักษณะนี้ได้เมื่อต้องการเพียงข้อมูลเมตาเกี่ยวกับไฟล์ แต่ไม่จำเป็นต้องส่งข้อมูลทั้งหมดของไฟล์
ไม่บังคับ: ดูขนาดของทรัพยากร
มาดูส่วนหัวของการตอบกลับการดึงข้อมูลสำหรับ examples/words.txt
เพื่อกำหนดขนาดของไฟล์กัน
อัปเดตฟังก์ชัน headRequest
เพื่อบันทึกพร็อพเพอร์ตี้ content-length
ของการตอบกลับ headers
(คำใบ้: ดูเอกสารประกอบส่วนหัวและเมธอด get)
หลังจากอัปเดตโค้ดแล้ว ให้บันทึกไฟล์และรีเฟรชหน้าเว็บ คลิกคำขอ HEAD คอนโซลควรบันทึกขนาด (เป็นไบต์) ของ examples/words.txt
คำอธิบาย
ในตัวอย่างนี้ มีการใช้วิธี HEAD เพื่อขอขนาด (เป็นไบต์) ของทรัพยากร (แสดงในส่วนหัว content-length
) โดยไม่ต้องโหลดทรัพยากรจริง ในทางปฏิบัติ คุณสามารถใช้ข้อมูลนี้เพื่อพิจารณาว่าควรขอทรัพยากรแบบเต็มหรือไม่ (หรือแม้กระทั่งวิธีขอ)
ไม่บังคับ: ค้นหาขนาดของ examples/words.txt
โดยใช้วิธีอื่น และยืนยันว่าตรงกับค่าจากส่วนหัวของการตอบกลับ (คุณสามารถค้นหาวิธีดำเนินการนี้สำหรับระบบปฏิบัติการที่เฉพาะเจาะจงของคุณได้ ซึ่งเป็นโบนัสสำหรับการใช้บรรทัดคำสั่ง)
สำหรับข้อมูลเพิ่มเติม
นอกจากนี้ Fetch ยังส่งข้อมูลด้วยคำขอ POST ได้ด้วย
ตั้งค่าเซิร์ฟเวอร์ Echo
ในตัวอย่างนี้ คุณต้องเรียกใช้เซิร์ฟเวอร์ Echo จากไดเรกทอรี fetch-api-lab/app/
ให้เรียกใช้คำสั่งต่อไปนี้ (หากเซิร์ฟเวอร์ localhost:8081
บล็อกบรรทัดคำสั่ง ให้เปิดหน้าต่างหรือแท็บบรรทัดคำสั่งใหม่)
node echo-servers/cors-server.js
คำสั่งนี้จะเริ่มเซิร์ฟเวอร์อย่างง่ายที่ localhost:5000/
ซึ่งจะแสดงคำขอที่ส่งไปยังเซิร์ฟเวอร์นั้นกลับมา
คุณสิ้นสุดการใช้งานเซิร์ฟเวอร์นี้ได้ทุกเมื่อด้วย ctrl+c
ส่งคำขอ POST
แทนที่ฟังก์ชัน postRequest
ด้วยโค้ดต่อไปนี้ (ตรวจสอบว่าคุณได้กำหนดฟังก์ชัน showText
จากส่วนที่ 4 แล้ว หากยังไม่ได้ทำ)
function postRequest() {
fetch('http://localhost:5000/', {
method: 'POST',
body: 'name=david&message=hello'
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คลิกคำขอ POST สังเกตคำขอที่ส่งซึ่งแสดงในหน้าเว็บ โดยควรมีชื่อและข้อความ (โปรดทราบว่าเรายังไม่ได้รับข้อมูลจากแบบฟอร์ม)
คำอธิบาย
หากต้องการส่งคำขอ POST ด้วยการเรียกข้อมูล เราจะใช้พารามิเตอร์ init
เพื่อระบุเมธอด (คล้ายกับวิธีที่เราตั้งค่าเมธอด HEAD ในส่วนก่อนหน้า) นอกจากนี้ เรายังตั้งค่าเนื้อหาของคำขอในส่วนนี้ด้วย ซึ่งในกรณีนี้คือสตริงอย่างง่าย เนื้อหาคือข้อมูลที่เราต้องการส่ง
หมายเหตุ: ในเวอร์ชันที่ใช้งานจริง โปรดอย่าลืมเข้ารหัสข้อมูลผู้ใช้ที่มีความละเอียดอ่อนเสมอ
เมื่อส่งข้อมูลเป็นคำขอ POST ไปยัง localhost:5000/
ระบบจะส่งคำขอดังกล่าวกลับมาเป็นคำตอบ จากนั้นระบบจะตรวจสอบคำตอบด้วย validateResponse
อ่านเป็นข้อความ และแสดงในหน้า
ในทางปฏิบัติ เซิร์ฟเวอร์นี้จะแสดง API ของบุคคลที่สาม
ไม่บังคับ: ใช้อินเทอร์เฟซ FormData
คุณสามารถใช้อินเทอร์เฟซ FormData เพื่อดึงข้อมูลจากแบบฟอร์มได้อย่างง่ายดาย
ในฟังก์ชัน postRequest
ให้สร้างออบเจ็กต์ FormData
ใหม่จากองค์ประกอบแบบฟอร์ม msg-form
ดังนี้
const formData = new FormData(document.getElementById('msg-form'));
จากนั้นแทนที่ค่าของพารามิเตอร์ body
ด้วยตัวแปร formData
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ กรอกแบบฟอร์ม (ช่องชื่อและข้อความ) ในหน้าเว็บ แล้วคลิกคำขอ POST สังเกตเนื้อหาแบบฟอร์มที่แสดงในหน้า
คำอธิบาย
เครื่องมือสร้าง FormData
สามารถรับ form
HTML และสร้างออบเจ็กต์ FormData
ได้ ระบบจะป้อนข้อมูลคีย์และค่าของแบบฟอร์มลงในออบเจ็กต์นี้
สำหรับข้อมูลเพิ่มเติม
เริ่มเซิร์ฟเวอร์ก้องที่ไม่ใช่ CORS
หยุดเซิร์ฟเวอร์ Echo ก่อนหน้า (โดยกด ctrl+c
จากบรรทัดคำสั่ง) แล้วเริ่มเซิร์ฟเวอร์ Echo ใหม่จากไดเรกทอรี fetch-lab-api/app/
โดยเรียกใช้คำสั่งต่อไปนี้
node echo-servers/no-cors-server.js
คำสั่งนี้จะตั้งค่าเซิร์ฟเวอร์ Echo แบบง่ายอีกเซิร์ฟเวอร์หนึ่ง ซึ่งคราวนี้อยู่ที่ localhost:5001/
อย่างไรก็ตาม เซิร์ฟเวอร์นี้ไม่ได้กำหนดค่าให้ยอมรับคำขอข้ามโดเมน
ดึงข้อมูลจากเซิร์ฟเวอร์ใหม่
ตอนนี้เซิร์ฟเวอร์ใหม่ทำงานที่ localhost:5001/
แล้ว เราจึงส่งคำขอเรียกข้อมูลไปยังเซิร์ฟเวอร์นั้นได้
อัปเดตฟังก์ชัน postRequest
เพื่อดึงข้อมูลจาก localhost:5001/
แทน localhost:5000/
หลังจากอัปเดตโค้ดแล้ว ให้บันทึกไฟล์ รีเฟรชหน้าเว็บ แล้วคลิกคำขอ POST
คุณควรได้รับข้อผิดพลาดในคอนโซลที่ระบุว่าคำขอข้ามต้นทางถูกบล็อกเนื่องจากไม่มีส่วนหัว Access-Control-Allow-Origin
ของ CORS
อัปเดต fetch
ในฟังก์ชัน postRequest
ด้วยโค้ดต่อไปนี้ ซึ่งใช้โหมด no-cors (ตามที่บันทึกข้อผิดพลาดแนะนำ) และนำการเรียกใช้ validateResponse
และ readResponseAsText
ออก (ดูคำอธิบายด้านล่าง)
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5001/', {
method: 'POST',
body: formData,
mode: 'no-cors'
})
.then(logResult)
.catch(logError);
}
บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ จากนั้นกรอกแบบฟอร์มข้อความแล้วคลิกคำขอ POST
สังเกตออบเจ็กต์การตอบกลับที่บันทึกไว้ในคอนโซล
คำอธิบาย
Fetch (และ XMLHttpRequest) เป็นไปตามนโยบายแหล่งที่มาเดียวกัน ซึ่งหมายความว่าเบราว์เซอร์จะจำกัดคำขอ HTTP แบบข้ามแหล่งที่มาจากภายในสคริปต์ คำขอข้ามแหล่งที่มาจะเกิดขึ้นเมื่อโดเมนหนึ่ง (เช่น http://foo.com/
) ขอทรัพยากรจากโดเมนอื่น (เช่น http://bar.com/
)
หมายเหตุ: ข้อจำกัดของคำขอข้ามต้นทางมักเป็นจุดที่ทำให้เกิดความสับสน ระบบจะดึงข้อมูลทรัพยากรจำนวนมาก เช่น รูปภาพ สไตล์ชีต และสคริปต์ จากโดเมนต่างๆ (เช่น ข้ามต้นทาง) อย่างไรก็ตาม สิ่งเหล่านี้เป็นข้อยกเว้นของนโยบายต้นทางเดียวกัน คำขอข้ามต้นทางยังคงถูกจำกัดจากภายในสคริปต์
เนื่องจากเซิร์ฟเวอร์ของแอปมีหมายเลขพอร์ตที่แตกต่างจากเซิร์ฟเวอร์ Echo ทั้ง 2 เซิร์ฟเวอร์ คำขอที่ส่งไปยังเซิร์ฟเวอร์ Echo เซิร์ฟเวอร์ใดเซิร์ฟเวอร์หนึ่งจึงถือว่าเป็นคำขอข้ามต้นทาง อย่างไรก็ตาม เซิร์ฟเวอร์ Echo แรกที่ทำงานบน localhost:5000/
ได้รับการกำหนดค่าให้รองรับ CORS (คุณสามารถเปิด echo-servers/cors-server.js
และตรวจสอบการกำหนดค่าได้) เซิร์ฟเวอร์ Echo ใหม่ที่ทำงานบน localhost:5001/
ไม่ได้ (ซึ่งเป็นสาเหตุที่เราได้รับข้อผิดพลาด)
การใช้ mode: no-cors
จะช่วยให้ดึงข้อมูลการตอบกลับแบบทึบแสงได้ ซึ่งจะช่วยให้เราได้รับการตอบกลับ แต่จะป้องกันการเข้าถึงการตอบกลับด้วย JavaScript (ซึ่งเป็นเหตุผลที่เราใช้ validateResponse
, readResponseAsText
หรือ showResponse
ไม่ได้) การตอบกลับยังคงใช้ได้กับ API อื่นๆ หรือแคชโดย Service Worker
แก้ไขส่วนหัวของคำขอ
นอกจากนี้ Fetch ยังรองรับการแก้ไขส่วนหัวของคำขอด้วย หยุดเซิร์ฟเวอร์ก้อง localhost:5001
(ไม่มี CORS) และรีสตาร์ทเซิร์ฟเวอร์ก้อง localhost:5000
(CORS) จากส่วนที่ 6:
node echo-servers/cors-server.js
กู้คืนฟังก์ชัน postRequest
เวอร์ชันก่อนหน้าซึ่งดึงข้อมูลจาก localhost:5000/
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5000/', {
method: 'POST',
body: formData
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
ตอนนี้ให้ใช้อินเทอร์เฟซส่วนหัวเพื่อสร้างออบเจ็กต์ส่วนหัวภายในฟังก์ชัน postRequest
ที่ชื่อ messageHeaders
โดยมีส่วนหัว Content-Type
เท่ากับ application/json
จากนั้นตั้งค่าพร็อพเพอร์ตี้ headers
ของออบเจ็กต์ init
ให้เป็นตัวแปร messageHeaders
อัปเดตพร็อพเพอร์ตี้ body
ให้เป็นออบเจ็กต์ JSON ที่แปลงเป็นสตริงแล้ว เช่น
JSON.stringify({ lab: 'fetch', status: 'fun' })
หลังจากอัปเดตโค้ดแล้ว ให้บันทึกไฟล์และรีเฟรชหน้าเว็บ จากนั้นคลิกส่งคำขอ POST
สังเกตว่าคำขอที่ส่งกลับมาตอนนี้มี Content-Type
เป็น application/json
(แทนที่จะเป็น multipart/form-data
เหมือนก่อนหน้านี้)
ตอนนี้ให้เพิ่มContent-Length
ส่วนหัวที่กำหนดเองmessageHeaders
ลงในออบเจ็กต์ messageHeaders
แล้วกำหนดขนาดตามต้องการให้กับคำขอ
หลังจากอัปเดตโค้ดแล้ว ให้บันทึกไฟล์ รีเฟรชหน้าเว็บ แล้วคลิกคำขอ POST โปรดสังเกตว่าส่วนหัวนี้จะไม่ได้รับการแก้ไขในคำขอที่ส่งกลับ
คำอธิบาย
อินเทอร์เฟซส่วนหัวช่วยให้สร้างและแก้ไขออบเจ็กต์ส่วนหัวได้ ส่วนหัวบางรายการ เช่น Content-Type
สามารถแก้ไขได้โดยการดึงข้อมูล ส่วนอื่นๆ เช่น Content-Length
จะได้รับการป้องกันและแก้ไขไม่ได้ (เพื่อเหตุผลด้านความปลอดภัย)
ตั้งค่าส่วนหัวคำขอที่กำหนดเอง
Fetch รองรับการตั้งค่าส่วนหัวที่กำหนดเอง
นำส่วนหัว Content-Length
ออกจากออบเจ็กต์ messageHeaders
ในฟังก์ชัน postRequest
เพิ่มส่วนหัวที่กำหนดเอง X-Custom
โดยมีค่าใดก็ได้ (เช่น 'X-CUSTOM': 'hello world'
)
บันทึกสคริปต์ รีเฟรชหน้าเว็บ แล้วคลิกคำขอ POST
คุณควรเห็นว่าคำขอที่ส่งกลับมีพร็อพเพอร์ตี้ X-Custom
ที่คุณเพิ่ม
ตอนนี้ให้เพิ่มY-Custom
ส่วนหัวไปยังออบเจ็กต์ส่วนหัว บันทึกสคริปต์ รีเฟรชหน้าเว็บ แล้วคลิกคำขอ POST
คุณควรได้รับข้อผิดพลาดที่คล้ายกับข้อความต่อไปนี้ในคอนโซล
Fetch API cannot load http://localhost:5000/. Request header field y-custom is not allowed by Access-Control-Allow-Headers in preflight response.
คำอธิบาย
เช่นเดียวกับคำขอข้ามแหล่งที่มา เซิร์ฟเวอร์ที่ขอทรัพยากรต้องรองรับส่วนหัวที่กำหนดเอง ในตัวอย่างนี้ เราได้กำหนดค่าเซิร์ฟเวอร์ Echo ให้ยอมรับส่วนหัว X-Custom
แต่ไม่ยอมรับส่วนหัว Y-Custom
(คุณสามารถเปิด echo-servers/cors-server.js
แล้วมองหา Access-Control-Allow-Headers
เพื่อดูด้วยตนเอง) ทุกครั้งที่มีการตั้งค่าส่วนหัวที่กำหนดเอง เบราว์เซอร์จะทำการตรวจสอบก่อนส่งคำขอ ซึ่งหมายความว่าเบราว์เซอร์จะส่งคำขอ OPTIONS ไปยังเซิร์ฟเวอร์ก่อนเพื่อพิจารณาว่าเซิร์ฟเวอร์อนุญาตให้ใช้เมธอดและส่วนหัว HTTP ใด หากเซิร์ฟเวอร์ได้รับการกำหนดค่าให้ยอมรับเมธอดและส่วนหัวของคำขอเดิม ระบบจะส่งคำขอ แต่หากไม่เป็นเช่นนั้น ระบบจะแสดงข้อผิดพลาด
สำหรับข้อมูลเพิ่มเติม
รหัสโซลูชัน
หากต้องการรับสำเนาของโค้ดที่ใช้งานได้ ให้ไปที่โฟลเดอร์โซลูชัน
ตอนนี้คุณก็ทราบวิธีใช้ Fetch API แล้ว
แหล่งข้อมูล
หากต้องการดู Codelab ทั้งหมดในหลักสูตรการฝึกอบรม PWA โปรดดู Codelab ต้อนรับสำหรับหลักสูตร