การเพิ่มการรองรับ CORS ไปยังพร็อกซี API

คุณกำลังดูเอกสารประกอบของ Apigee Edge
ไปที่เอกสารประกอบของ Apigee X
ข้อมูล

CORS (การแชร์ทรัพยากรข้ามต้นทาง) เป็นกลไกมาตรฐานที่อนุญาตให้เรียกใช้ JavaScript XMLHttpRequest (XHR) ในหน้าเว็บเพื่อโต้ตอบกับทรัพยากรจากโดเมนที่ไม่ใช่ต้นทาง CORS เป็นโซลูชันที่ใช้กันโดยทั่วไปสำหรับ "นโยบายต้นทางเดียวกัน" ซึ่งบังคับใช้โดยเบราว์เซอร์ทั้งหมด เช่น หากคุณทำการเรียก XHR ไปยัง Twitter API จากโค้ด JavaScript ที่ดำเนินการในเบราว์เซอร์ การเรียกจะล้มเหลว เนื่องจากโดเมนที่แสดงหน้าเว็บในเบราว์เซอร์ของคุณไม่เหมือนกับโดเมนที่ให้บริการ Twitter API CORS จะแก้ปัญหานี้โดยอนุญาตให้เซิร์ฟเวอร์ "เลือกใช้" หากต้องการแชร์ทรัพยากรแบบข้ามต้นทาง

วิดีโอ: ดูวิดีโอสั้นๆ เพื่อดูวิธีเปิดใช้ CORS บนพร็อกซี API

กรณีการใช้งานทั่วไปสำหรับ CORS

โค้ด JQuery ต่อไปนี้เรียกใช้บริการเป้าหมายที่สมมติขึ้น หากดำเนินการจากภายในบริบทของเบราว์เซอร์ (หน้าเว็บ) การเรียกใช้จะล้มเหลวเนื่องจากนโยบายต้นทางเดียวกัน

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

วิธีแก้ไขอย่างหนึ่งคือการสร้างพร็อกซี Apigee API ที่เรียกใช้ API ของบริการในเบื้องหลัง โปรดทราบว่า Edge อยู่ระหว่างไคลเอ็นต์ (ในกรณีนี้คือเบราว์เซอร์) และ API แบ็กเอนด์ (บริการ) เนื่องจากพร็อกซี API ทำงานบนเซิร์ฟเวอร์ ไม่ใช่ในเบราว์เซอร์ จึงเรียกใช้บริการได้สําเร็จ จากนั้น เพียงแค่แนบส่วนหัว CORS ไปในการตอบสนองของ TargetEndpoint ตราบใดที่เบราว์เซอร์รองรับ CORS ส่วนหัวเหล่านี้จะส่งสัญญาณให้เบราว์เซอร์ว่า "ผ่อนคลาย" กับนโยบายต้นทางเดียวกัน ซึ่งจะทำให้การเรียก API ข้ามต้นทางประสบความสำเร็จ

เมื่อสร้างพร็อกซีที่มีการสนับสนุน CORS แล้ว คุณจะเรียกใช้ URL พร็อกซี API แทนบริการแบ็กเอนด์ในโค้ดฝั่งไคลเอ็นต์ได้ เช่น

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

การแนบนโยบาย "เพิ่ม CORS" ไปยังพร็อกซี API ใหม่

คุณเพิ่มการรองรับ CORS ไปยังพร็อกซี API ได้โดยแนบนโยบาย "เพิ่ม CORS" ลงในพร็อกซี API เมื่อสร้าง หากต้องการเพิ่มนโยบายนี้ ให้เลือกช่องทำเครื่องหมายเพิ่มส่วนหัว CORS ในหน้าความปลอดภัยของวิซาร์ดสร้างพร็อกซี

เมื่อคุณเลือกช่องทำเครื่องหมายนี้ ระบบจะเพิ่มนโยบายที่ชื่อว่า "Add CORS" ลงในระบบโดยอัตโนมัติ และแนบกับโฟลว์การตอบสนองของ TargetEndpoint ดังที่แสดงในรูปต่อไปนี้

เพิ่มนโยบาย CORS ที่เพิ่มลงในตัวนำทางภายใต้นโยบาย และแนบกับการนำหน้าของการตอบกลับ TargetEndpoint ในเลื่อนไปทางขวา

นโยบาย "เพิ่ม CORS" จะดำเนินการเป็นนโยบาย AssignMessage ซึ่งจะเพิ่มส่วนหัวที่เหมาะสมในการตอบกลับ โดยพื้นฐานแล้ว ส่วนหัวจะช่วยให้เบราว์เซอร์รู้ว่าต้นทางใดที่จะแชร์ทรัพยากรให้ วิธีการที่ยอมรับ และอื่นๆ อ่านข้อมูลเพิ่มเติมเกี่ยวกับส่วนหัว CORS เหล่านี้ได้ในคำแนะนำ W3C เกี่ยวกับการแชร์ทรัพยากรข้ามต้นทาง

คุณควรแก้ไขนโยบายดังนี้

  • เพิ่มส่วนหัว content-type และ authorization (จำเป็นต่อการรองรับการตรวจสอบสิทธิ์พื้นฐานหรือ OAuth2) ในส่วนหัวของ Access-Control-Allow-Headers ดังที่แสดงในโค้ดที่ตัดตอนมาด้านล่าง
  • สำหรับการตรวจสอบสิทธิ์ OAuth2 คุณอาจต้องทำตามขั้นตอนเพื่อแก้ไขการทำงานที่ไม่เป็นไปตามข้อกำหนด RFC
  • เราขอแนะนำให้คุณใช้ <Set> เพื่อตั้งค่าส่วนหัว CORS แทน <Add> ดังที่แสดงในข้อความที่ตัดตอนมาด้านล่าง เมื่อใช้ <Add> หากมีส่วนหัว Access-Control-Allow-Origin อยู่แล้ว คุณจะได้รับข้อผิดพลาดต่อไปนี้

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    ดูข้อมูลเพิ่มเติมได้ที่ ข้อผิดพลาด CORS : ส่วนหัวมีหลายค่า "*, *" แต่อนุญาตเพียงค่าเดียว

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

การเพิ่มส่วนหัว CORS ในพร็อกซีที่มีอยู่

คุณต้องสร้างนโยบาย Assign Message ใหม่ด้วยตนเอง และคัดลอกโค้ดสำหรับนโยบาย "เพิ่ม CORS" ที่แสดงในส่วนก่อนหน้าไปยังนโยบายดังกล่าว จากนั้นแนบนโยบายกับโฟลว์การตอบสนองของ TargetEndpoint ของพร็อกซี API คุณสามารถแก้ไขค่าส่วนหัวได้ตามต้องการ ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้างและแนบนโยบายได้ที่นโยบายคืออะไร

การจัดการคำขอการตรวจสอบล่วงหน้าสำหรับ CORS

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

โดยปกติแล้ว คำขอการตรวจสอบล่วงหน้าของ CORS จะสร้างโดยใช้เมธอด HTTP OPTIONS เมื่อเซิร์ฟเวอร์ที่รองรับ CORS ได้รับคำขอ OPTIONS เซิร์ฟเวอร์จะส่งชุดส่วนหัว CORS ไปยังไคลเอ็นต์ซึ่งระบุระดับการรองรับ CORS แฮนด์เชคนี้ทำให้ไคลเอ็นต์รู้ว่าสามารถขออะไรจากโดเมนที่ไม่ใช่ต้นทางได้

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

Apigee ไม่ได้รวมโซลูชันการตรวจสอบล่วงหน้าสำหรับ CORS ไว้ให้ตั้งแต่แรก แต่ก็นำไปใช้งานได้ตามที่อธิบายไว้ในส่วนนี้ วัตถุประสงค์คือให้พร็อกซีประเมินคำขอ OPTIONS ในขั้นตอนการดำเนินการแบบมีเงื่อนไข จากนั้นพร็อกซีจะส่งการตอบกลับที่เหมาะสมกลับไปยังไคลเอ็นต์ได้

มาดูขั้นตอนตัวอย่างแล้วพูดคุยในส่วนที่จัดการคำขอการตรวจสอบล่วงหน้ากัน

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

ส่วนสำคัญของ ProxyEndpoint นี้มีดังนี้

  • ระบบจะสร้าง RouteRule ไปยังเป้าหมาย NULL ที่มีเงื่อนไขสำหรับคำขอ OPTIONS โปรดทราบว่าไม่มีการระบุ TargetEndpoint ถ้าได้รับคำขอ OPTIONS และส่วนหัวของคำขอ Origin และ Access-Control-Request-Method ไม่เป็นค่าว่าง พร็อกซีจะแสดงส่วนหัว CORS ทันทีในการตอบกลับไคลเอ็นต์ (ข้ามเป้าหมาย "แบ็กเอนด์" ที่เป็นค่าเริ่มต้นตามจริง) โปรดดูรายละเอียดเกี่ยวกับเงื่อนไขโฟลว์และ RouteRule ที่หัวข้อเงื่อนไขที่มีตัวแปรโฟลว์

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    
  • ระบบจะสร้างขั้นตอน OptionsPreFlight ที่เพิ่มนโยบาย Add CORS ที่มีส่วนหัว CORS ไปยังโฟลว์หากได้รับคำขอ OPTIONS และส่วนหัวของคำขอต้นทางและ Access-Control-Request-Method ไม่มีข้อมูล

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>
    

การใช้โซลูชัน CORS ตัวอย่าง

คุณสามารถดูตัวอย่างโซลูชัน CORS ที่ติดตั้งเป็นกระบวนการที่ใช้ร่วมกันได้ใน GitHub นำเข้าแพ็กเกจโฟลว์ที่แชร์ไปยังสภาพแวดล้อม และต่อเชื่อมโดยใช้ Flow hook หรือเข้ากับโฟลว์พร็อกซี API โดยตรง โปรดดูรายละเอียดที่ไฟล์ CORS-Shared-FLow README ที่ให้มาพร้อมกับตัวอย่าง