ช่วยให้เบราว์เซอร์ออกคำขอ XMLHttpRequest ไปยังเซิร์ฟเวอร์ข้ามแหล่งกำเนิดดังนั้นการเอาชนะข้อ จำกัด ที่ AJAX สามารถใช้ในแหล่งกำเนิดเดียวกันเท่านั้น
บทความนี้แนะนำกลไกภายในของ CORS โดยละเอียด
(คำอธิบายภาพถ่าย: ถ่ายที่ Oasis Park ใน Al Ain, UAE)
1. บทนำ
CORS ต้องการทั้งการสนับสนุนเบราว์เซอร์และเซิร์ฟเวอร์ ปัจจุบันเบราว์เซอร์ทั้งหมดรองรับฟังก์ชั่นนี้และเบราว์เซอร์เช่นไม่สามารถต่ำกว่า IE10
กระบวนการสื่อสารทั้งหมดของ CORS จะเสร็จสมบูรณ์โดยเบราว์เซอร์โดยอัตโนมัติและไม่ต้องการการมีส่วนร่วมของผู้ใช้ สำหรับนักพัฒนา CORS การสื่อสารไม่แตกต่างจากการสื่อสาร Ajax ที่มาเดียวกันและรหัสนั้นเหมือนกันทุกประการ เมื่อเบราว์เซอร์ค้นพบว่า AJAX ร้องขอข้ามต้นกำเนิดมันจะเพิ่มข้อมูลส่วนหัวเพิ่มเติมโดยอัตโนมัติและบางครั้งจะมีคำขอเพิ่มเติม แต่ผู้ใช้จะไม่รู้สึก
ดังนั้นกุญแจสำคัญในการใช้การสื่อสาร CORS คือเซิร์ฟเวอร์ ตราบใดที่เซิร์ฟเวอร์ใช้อินเทอร์เฟซ CORS การสื่อสารข้ามแหล่งกำเนิดสามารถดำเนินการได้
สองคำขอ
เบราว์เซอร์แบ่งคำขอ CORS ออกเป็นสองประเภท: คำของ่าย ๆ และคำขอไม่ง่าย
ตราบใดที่พบเงื่อนไขหลักสองประการต่อไปนี้ในเวลาเดียวกันก็เป็นคำของ่ายๆ
(1) วิธีการร้องขอเป็นหนึ่งในสามวิธีต่อไปนี้:
โพสต์ HeadgetPost
(2) ข้อมูลส่วนหัวของ HTTP ไม่เกินฟิลด์ต่อไปนี้:
Acceptaccept-languagecontent-languagelast-event-idcontent-type: จำกัด เพียงสามค่า application/x-www-form-urlencoded , multipart/form-data , text/plain
ใครก็ตามที่ไม่ตรงตามสองเงื่อนไขข้างต้นในเวลาเดียวกันคือคำขอที่ไม่ง่าย
การจัดการเบราว์เซอร์ของคำขอทั้งสองนี้แตกต่างกัน
3. คำของ่าย ๆ 3.1 กระบวนการพื้นฐาน
สำหรับการร้องขออย่างง่ายเบราว์เซอร์จะออกคำขอ CORS โดยตรง โดยเฉพาะเพิ่มฟิลด์ Origin ไปยังข้อมูลส่วนหัว
ต่อไปนี้เป็นตัวอย่าง เบราว์เซอร์พบว่าคำขอ Ajax ข้ามต้นนี้เป็นคำของ่าย ๆ และจะเพิ่มฟิลด์ Origin ไปยังข้อมูลส่วนหัวโดยอัตโนมัติ
GET /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
ในข้อมูลส่วนหัวด้านบนฟิลด์ Origin ใช้เพื่อระบุแหล่งที่มา (Protocol + ชื่อโดเมน + พอร์ต) คำขอมาจาก ตามค่านี้เซิร์ฟเวอร์ตัดสินใจว่าจะยอมรับคำขอหรือไม่
หากแหล่งที่มาที่ระบุโดย Origin ไม่ได้อยู่ในขอบเขตการอนุญาตเซิร์ฟเวอร์จะส่งคืนการตอบกลับ HTTP ปกติ เบราว์เซอร์พบว่าข้อมูลส่วนหัวของการตอบกลับนี้ไม่ได้มีฟิลด์ Access-Control-Allow-Origin (ดูรายละเอียดด้านล่าง) ดังนั้นจึงเป็นที่ทราบกันดีว่าเกิดข้อผิดพลาด XMLHttpRequest ทำให้เกิดข้อผิด onerror โปรดทราบว่าข้อผิดพลาดนี้ไม่สามารถระบุได้ด้วยรหัสสถานะเนื่องจากรหัสสถานะของการตอบกลับ HTTP อาจเป็น 200
หากชื่อโดเมนที่ระบุโดย Origin อยู่ในขอบเขตการอนุญาตการตอบกลับที่ส่งคืนโดยเซิร์ฟเวอร์จะมีฟิลด์ส่วนหัวเพิ่มเติมหลายฟิลด์
Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarContent-Type: text/html; charset=utf-8
ในบรรดาข้อมูลส่วนหัวด้านบนมีสามฟิลด์ที่เกี่ยวข้องกับคำขอ CORS ทั้งหมดเริ่มต้นด้วย Access-Control- -
(1) การควบคุมการเข้าร่วม-Origin
ต้องการฟิลด์นี้ ค่าของมันคือค่าของฟิลด์ Origin ณ เวลาที่ร้องขอหรือ A * ระบุว่าคำขอสำหรับชื่อโดเมนใด ๆ ได้รับการยอมรับ
(2) การควบคุมการควบคุม-อนุญาต
ฟิลด์นี้เป็นทางเลือก ค่าของมันคือค่าบูลีนที่ระบุว่าจะอนุญาตให้ส่งคุกกี้หรือไม่ โดยค่าเริ่มต้นคุกกี้จะไม่รวมอยู่ในคำขอ CORS ตั้งค่าเป็น true ซึ่งหมายความว่าเซิร์ฟเวอร์อนุญาตอย่างชัดเจน คุกกี้สามารถรวมอยู่ในคำขอและส่งไปยังเซิร์ฟเวอร์ด้วยกัน ค่านี้สามารถตั้งค่าเป็น true เท่านั้น หากเซิร์ฟเวอร์ไม่ส่งคุกกี้โดยเบราว์เซอร์ให้ลบฟิลด์
(3) หัวควบคุมการเข้าถึง
ฟิลด์นี้เป็นทางเลือก เมื่อร้องขอ CORS วิธี getResponseHeader() ของวัตถุ XMLHttpRequest สามารถรับ 6 ฟิลด์พื้นฐาน: Cache-Control , Content-Language , Content-Type , Expires , Last-Modified , Pragma หากคุณต้องการรับฟิลด์อื่นคุณต้องระบุพวกเขาใน Access-Control-Expose-Headers ตัวอย่างข้างต้นระบุว่า getResponseHeader('FooBar') สามารถส่งคืนค่าของฟิลด์ FooBar
3.2 แอตทริบิวต์ Withcredentials
ดังที่ได้กล่าวไว้ข้างต้นคำขอ CORS จะไม่ส่งคุกกี้และข้อมูลการตรวจสอบความถูกต้อง HTTP โดยค่าเริ่มต้น หากคุณต้องการส่งคุกกี้ไปยังเซิร์ฟเวอร์ในมือข้างหนึ่งคุณต้องใช้เซิร์ฟเวอร์เพื่อตกลงและระบุฟิลด์ Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true ในทางกลับกันผู้พัฒนาจะต้องเปิดคุณสมบัติ withCredentials ในคำขอ AJAX
var xhr = new XMLHttpRequest();xhr.withCredentials = true;มิฉะนั้นเบราว์เซอร์จะไม่ส่งแม้ว่าเซิร์ฟเวอร์ตกลงที่จะส่งคุกกี้ อีกทางเลือกหนึ่งเซิร์ฟเวอร์ต้องตั้งค่าคุกกี้และเบราว์เซอร์จะไม่จัดการ
อย่างไรก็ตามหากการตั้งค่า withCredentials ถูกละเว้นเบราว์เซอร์บางตัวจะยังคงส่งคุกกี้เข้าด้วยกัน ในเวลานี้คุณสามารถปิดอย่างชัดเจน withCredentials
xhr.withCredentials = false; ควรสังเกตว่าหากคุณต้องการส่งคุกกี้ Access-Control-Allow-Origin ไม่สามารถตั้งค่าเป็นเครื่องหมายดอกจันและคุณต้องระบุชื่อโดเมนที่ชัดเจนซึ่งสอดคล้องกับหน้าเว็บที่ร้องขอ ในเวลาเดียวกันคุกกี้ยังคงเป็นไปตามนโยบายของต้นกำเนิดเดียวกัน เท่านั้นที่ตั้งค่าคุกกี้ที่มีชื่อโดเมนเซิร์ฟเวอร์เท่านั้นที่จะถูกอัปโหลด คุกกี้จากชื่อโดเมนอื่น ๆ จะไม่ถูกอัปโหลดและ document.cookie ในรหัสหน้าเว็บต้นฉบับ (ข้ามต้นฉบับ) ไม่สามารถอ่านคุกกี้ภายใต้ชื่อโดเมนเซิร์ฟเวอร์
4. คำขอที่ไม่ง่าย 4.1 คำขอก่อนการบิน
คำขอที่ไม่ง่ายเป็นคำขอที่มีข้อกำหนดพิเศษสำหรับเซิร์ฟเวอร์เช่นวิธีการ PUT หรือ DELETE หรือประเภทฟิลด์ Content-Type คือ application/json
คำขอ CORS ที่ไม่ใช่คำของ่ายๆจะเพิ่มคำขอสอบถาม HTTP ก่อนการสื่อสารอย่างเป็นทางการซึ่งเรียกว่าคำขอ "preflight"
เบราว์เซอร์แรกถามเซิร์ฟเวอร์ว่าชื่อโดเมนที่หน้าเว็บปัจจุบันอยู่ในรายการใบอนุญาตของเซิร์ฟเวอร์หรือไม่และสามารถใช้คำกริยา HTTP และฟิลด์ส่วนหัวได้ เฉพาะเมื่อได้รับการตอบกลับในเชิงบวกเบราว์เซอร์จะออกคำขอ XMLHttpRequest อย่างเป็นทางการมิฉะนั้นจะมีการรายงานข้อผิดพลาด
ด้านล่างเป็นสคริปต์ JavaScript เบราว์เซอร์
var url = 'http://api.alice.com/cors';var xhr = new XMLHttpRequest();xhr.open('PUT', url, true);xhr.setRequestHeader('X-Custom-Header', 'value');xhr.send();
ในรหัสข้างต้นวิธีการขอ HTTP จะ PUT และส่งข้อมูลส่วนหัวที่กำหนดเอง X-Custom-Header
เบราว์เซอร์พบว่านี่เป็นคำขอที่ไม่ง่ายดังนั้นจึงออกคำขอ "ก่อนการบิน" โดยอัตโนมัติซึ่งกำหนดให้เซิร์ฟเวอร์ยืนยันว่าสามารถร้องขอได้ ด้านล่างเป็นข้อมูลส่วนหัว HTTP สำหรับคำขอ "preflight" นี้
OPTIONS /cors HTTP/1.1Origin: http://api.bob.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-HeaderHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0... วิธีการร้องขอที่ใช้โดยคำขอ "preflight" คือ OPTIONS ซึ่งระบุว่าคำขอนี้ใช้เพื่อสอบถาม ในข้อมูลส่วนหัวฟิลด์คีย์คือ Origin ซึ่งระบุแหล่งที่มาของคำขอมาจาก
นอกเหนือจากฟิลด์ Origin ข้อมูลส่วนหัวของคำขอ "preflight" ยังมีสองฟิลด์พิเศษ
(1) การควบคุมการเข้าร่วม
ฟิลด์นี้จำเป็นต้องมีรายการวิธี HTTP ที่จะใช้สำหรับคำขอ CORS ของเบราว์เซอร์ ตัวอย่างข้างต้นถูก PUT
(2) ผู้ควบคุมการเข้าถึง
ฟิลด์นี้เป็นสตริงที่คั่นด้วยเครื่องหมายจุลภาคซึ่งระบุฟิลด์ข้อมูลส่วนหัวเพิ่มเติมที่คำขอเบราว์เซอร์ Cors จะส่ง ตัวอย่างข้างต้นคือ X-Custom-Header
4.2 การตอบสนองต่อคำขอก่อนการบิน
หลังจากเซิร์ฟเวอร์ได้รับคำขอ "preflight" หลังจากตรวจสอบ Origin แล้ว Access-Control-Request-Method และ Access-Control-Request-Headers ยืนยันว่าอนุญาตให้มีการร้องขอข้ามแหล่งกำเนิดและคุณสามารถตอบกลับได้
HTTP/1.1 200 OKDate: Mon, 01 Dec 2008 01:15:39 GMTServer: Apache/2.0.61 (Unix)Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderContent-Type: text/html; charset=utf-8Content-Encoding: gzipContent-Length: 0Keep-Alive: timeout=2, max=100Connection: Keep-AliveContent-Type: text/plain
ในการตอบกลับ HTTP ด้านบนคีย์คือฟิลด์ Access-Control-Allow-Origin ซึ่งหมายความว่า http://api.bob.com สามารถขอข้อมูลได้ ฟิลด์นี้ยังสามารถตั้งค่าเป็นเครื่องหมายดอกจันเพื่อยอมรับคำขอข้ามแหล่งกำเนิดใด ๆ
Access-Control-Allow-Origin: *
หากเบราว์เซอร์คัดค้านคำขอ "preflight" การตอบสนอง HTTP ปกติจะถูกส่งคืน แต่ไม่มีฟิลด์ส่วนหัวที่เกี่ยวข้องกับ CORS ในเวลานี้เบราว์เซอร์จะพิจารณาว่าเซิร์ฟเวอร์ไม่เห็นด้วยกับคำขอ preflight ดังนั้นข้อผิดพลาดจะถูกทริกเกอร์และจับโดยฟังก์ชั่นการโทรกลับของ onerror ของวัตถุ XMLHttpRequest คอนโซลจะพิมพ์ข้อความแสดงข้อผิดพลาดต่อไปนี้
XMLHttpRequest cannot load http://api.alice.com.Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
ฟิลด์ที่เกี่ยวข้องกับ CORS อื่น ๆ ที่เซิร์ฟเวอร์ตอบสนองมีดังนี้
Access-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderAccess-Control-Allow-Credentials: trueAccess-Control-Max-Age: 1728000(1) วิธีการควบคุมการเข้าถึง-อนุญาต
ต้องการฟิลด์นี้และค่าของมันคือสตริงที่คั่นด้วยเครื่องหมายจุลภาคซึ่งระบุวิธีการขอข้ามโดเมนทั้งหมดที่รองรับโดยเซิร์ฟเวอร์ โปรดทราบว่าวิธีการที่รองรับทั้งหมดจะถูกส่งคืนไม่ใช่แค่วิธีที่เบราว์เซอร์ร้องขอ นี่คือการหลีกเลี่ยงคำขอ "preflight" หลายรายการ
(2) หัวควบคุมการเข้าถึง
หากคำขอเบราว์เซอร์รวมถึง Access-Control-Request-Headers จำเป็นต้องใช้ฟิลด์ Access-Control-Allow-Headers นอกจากนี้ยังเป็นสตริงที่คั่นด้วยเครื่องหมายจุลภาคซึ่งระบุว่าฟิลด์ส่วนหัวทั้งหมดที่รองรับโดยเซิร์ฟเวอร์ไม่ จำกัด เฉพาะฟิลด์ที่เบราว์เซอร์ร้องขอใน "preflight"
(3) การควบคุมการควบคุม-อนุญาต
ฟิลด์นี้มีความหมายเช่นเดียวกับเมื่อมีการร้องขออย่างง่าย ๆ
(4) Access-Control-Max-age
ฟิลด์นี้เป็นทางเลือกและใช้เพื่อระบุระยะเวลาความถูกต้องของคำขอ preflight นี้ในไม่กี่วินาที ในผลลัพธ์ข้างต้นระยะเวลาความถูกต้องคือ 20 วัน (1728,000 วินาที) ซึ่งหมายความว่าการตอบสนองถูกแคชเป็นเวลา 1728,000 วินาที (เช่น 20 วัน) ในช่วงเวลานี้ไม่จำเป็นต้องมีคำขอ preflight อื่น ๆ
4.3 คำขอและคำตอบปกติจากเบราว์เซอร์
เมื่อเซิร์ฟเวอร์ผ่านคำขอ "preflight" ทุกครั้งที่คำขอ CORS ปกติของเบราว์เซอร์จะเป็นเช่นเดียวกับคำของ่าย ๆ จะมีฟิลด์ข้อมูลส่วนหัวต้น Origin การตอบสนองของเซิร์ฟเวอร์จะมีฟิลด์ส่วนหัวของ Access-Control-Allow-Origin
ด้านล่างนี้เป็นคำขอ CORS ปกติของเบราว์เซอร์หลังจากคำขอ "preflight"
PUT /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comX-Custom-Header: valueAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
ฟิลด์ Origin ของข้อมูลส่วนหัวข้างต้นจะถูกเพิ่มโดยเบราว์เซอร์โดยอัตโนมัติ
ด้านล่างคือการตอบสนองปกติจากเซิร์ฟเวอร์
Access-Control-Allow-Origin: http://api.bob.comContent-Type: text/html; charset=utf-8
ในข้อมูลส่วนหัวด้านบนจะต้องรวมฟิลด์ Access-Control-Allow-Origin ไว้ในการตอบสนองแต่ละครั้ง
5. เปรียบเทียบกับ JSONP
CORS ใช้ในจุดประสงค์เดียวกับ JSONP แต่มันมีพลังมากกว่า JSONP
JSONP รองรับการร้องขอ GET เท่านั้น CORS รองรับคำขอ HTTP ทุกประเภท ข้อได้เปรียบของ JSONP คือรองรับเบราว์เซอร์สมัยเก่าและสามารถขอข้อมูลจากเว็บไซต์ที่ไม่รองรับ CORS
(เกิน)