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 ต้อนรับสำหรับหลักสูตร