กำลังพยายามใช้การดึงข้อมูลและส่งผ่านในโหมด: no-cors


167

ฉันสามารถเข้าถึงจุดสิ้นสุดนี้http://catfacts-api.appspot.com/api/facts?number=99ผ่านบุรุษไปรษณีย์และได้รับผลตอบแทนJSON

นอกจากนี้ฉันกำลังใช้ create-react-app และต้องการหลีกเลี่ยงการตั้งค่าเซิร์ฟเวอร์ใด ๆ

ในรหัสลูกค้าของฉันฉันพยายามที่จะใช้fetchเพื่อทำสิ่งเดียวกัน แต่ฉันได้รับข้อผิดพลาด:

ไม่มีส่วนหัว 'Access-Control-Allow-Origin' บนทรัพยากรที่ร้องขอ แหล่งกำเนิด ' http: // localhost: 3000 ' จึงไม่ได้รับอนุญาตให้เข้าถึง หากการตอบกลับทึบแสงตอบสนองความต้องการของคุณให้ตั้งค่าโหมดคำขอเป็น 'no-cors' เพื่อดึงข้อมูลทรัพยากรโดยปิดการใช้งาน CORS

ดังนั้นฉันพยายามส่งผ่านวัตถุไปยัง Fetch ของฉันซึ่งจะปิดใช้งาน CORS ดังนี้:

fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });

น่าสนใจพอที่ข้อผิดพลาดที่ฉันได้รับเป็นข้อผิดพลาดทางไวยากรณ์ของฟังก์ชันนี้ ฉันไม่แน่ใจว่าจริงของฉันfetchเสียเพราะเมื่อฉันลบวัตถุ {mode: 'no-cors'} และจัดหา URL อื่นให้ทำงานได้ดี

ฉันยังพยายามส่งผ่านวัตถุ{ mode: 'opaque'}แต่สิ่งนี้ส่งกลับข้อผิดพลาดเดิมจากด้านบน

ฉันเชื่อในสิ่งที่ฉันต้องทำคือปิดการใช้งาน CORS .. ฉันขาดอะไรไป?

คำตอบ:


292

mode: 'no-cors'จะไม่ทำให้สิ่งต่าง ๆ ทำงานได้อย่างน่าอัศจรรย์ ในความเป็นจริงมันทำให้สิ่งเลวร้ายลงเพราะมีผลอย่างหนึ่งที่จะบอกเบราว์เซอร์“ บล็อกโค้ด JavaScript ส่วนหน้าของฉันจากการดูเนื้อหาของส่วนตอบสนองและส่วนหัวภายใต้สถานการณ์ทั้งหมด” แน่นอนว่าคุณแทบไม่ต้องการสิ่งนั้นเลย

เกิดอะไรขึ้นกับคำขอข้ามที่มาจากส่วนหน้า JavaScript คือเบราว์เซอร์โดยรหัสเริ่มต้นบล็อกส่วนหน้าจากการเข้าถึงแหล่งข้อมูลข้ามแหล่ง หากAccess-Control-Allow-Originอยู่ในการตอบสนองเบราว์เซอร์จะผ่อนคลายการบล็อกและอนุญาตให้โค้ดของคุณเข้าถึงการตอบกลับ

แต่ถ้าไซต์ไม่ส่งAccess-Control-Allow-Originคำตอบโค้ดส่วนหน้าของคุณจะไม่สามารถเข้าถึงการตอบสนองโดยตรงจากไซต์นั้น โดยเฉพาะอย่างยิ่งคุณไม่สามารถแก้ไขได้โดยการระบุmode: 'no-cors'(อันที่จริงแล้วจะทำให้แน่ใจว่าโค้ดส่วนหน้าของคุณไม่สามารถเข้าถึงเนื้อหาการตอบกลับได้)

แต่สิ่งหนึ่งที่จะทำงานถ้าคุณส่งคำขอของคุณผ่านพร็อกซี่ล ธเช่นนี้

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
    targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });
<pre></pre>

หมายเหตุ: หากคุณลองใช้ https://cors-anywhere.herokuapp.com คุณจะพบว่ามันหยุดทำงานคุณสามารถปรับใช้ proxy ของคุณกับ Heroku ได้อย่างง่ายดายในเวลาเพียง 2-3 นาทีโดยมี 5 คำสั่ง:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

หลังจากใช้คำสั่งเหล่านั้นคุณจะจบลงด้วยเซิร์ฟเวอร์ล ธ ทุกที่ของคุณเองทำงานที่เช่นhttps://cryptic-headland-94862.herokuapp.com/ ดังนั้นแทนที่จะนำหน้า URL คำขอของคุณด้วยhttps://cors-anywhere.herokuapp.comนำหน้าแทนด้วย URL สำหรับอินสแตนซ์ของคุณเอง เช่นhttps://cryptic-headland-94862.herokuapp.com/https://example.com


ฉันสามารถเข้าถึงจุดสิ้นสุดนี้http://catfacts-api.appspot.com/api/facts?number=99ผ่านบุรุษไปรษณีย์ได้

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORSอธิบายว่าทำไมถึงเป็นแม้ว่าคุณจะสามารถเข้าถึงการตอบกลับกับบุรุษไปรษณีย์ได้ แต่เบราว์เซอร์จะไม่ยอมให้คุณเข้าถึงการตอบสนองข้ามส่วนหน้าจากส่วนหน้า รหัส JavaScript ที่ทำงานในเว็บแอพเว้นแต่ว่าการตอบกลับจะรวมAccess-Control-Allow-Originหัวข้อการตอบกลับไว้ด้วย

http://catfacts-api.appspot.com/api/facts?number=99ไม่มีAccess-Control-Allow-Originส่วนหัวการตอบสนองดังนั้นจึงไม่มีวิธีที่รหัสส่วนหน้าของคุณสามารถเข้าถึงการตอบสนองข้ามจุดกำเนิดได้

เบราว์เซอร์ของคุณสามารถตอบสนองได้ดีและคุณสามารถดูได้ในบุรุษไปรษณีย์และแม้แต่ในเบราว์เซอร์ devtools - แต่นั่นไม่ได้หมายความว่าเบราว์เซอร์จะเปิดเผยรหัสของคุณ พวกเขาจะไม่ได้เพราะมันไม่มีAccess-Control-Allow-Originส่วนหัวตอบสนอง ดังนั้นคุณต้องใช้พร็อกซีเพื่อรับมันแทน

พร็อกซีทำการร้องขอไปยังไซต์นั้นรับการตอบสนองเพิ่มAccess-Control-Allow-Originส่วนหัวการตอบกลับและส่วนหัว CORS อื่น ๆ ที่จำเป็นจากนั้นส่งต่อกลับไปยังรหัสที่คุณร้องขอ และการตอบสนองที่Access-Control-Allow-Originเพิ่มส่วนหัวนั้นเป็นสิ่งที่เบราว์เซอร์เห็นดังนั้นเบราว์เซอร์จึงอนุญาตให้โค้ดส่วนหน้าของคุณเข้าถึงการตอบสนองได้


ดังนั้นฉันพยายามส่งผ่านวัตถุไปยัง Fetch ของฉันซึ่งจะปิดใช้งาน CORS

คุณไม่ต้องการทำเช่นนั้น เพื่อให้ชัดเจนเมื่อคุณบอกว่าคุณต้องการ“ ปิดการใช้งาน CORS” ดูเหมือนว่าคุณหมายถึงคุณต้องการปิดใช้งานนโยบายต้นทางเดียวกัน CORS เองเป็นวิธีการทำเช่นนั้น - CORS เป็นวิธีการคลายนโยบายที่มาดั้งเดิมไม่ใช่วิธีที่จะ จำกัด

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

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

คุณมักจะไม่ต้องการใช้mode: 'no-cors'ในทางปฏิบัติยกเว้นในบางกรณีที่ จำกัดและแม้ว่าคุณจะรู้ว่าสิ่งที่คุณทำและผลกระทบคืออะไร นั่นเป็นเพราะสิ่งที่การตั้งค่าmode: 'no-cors'จริงพูดกับเบราว์เซอร์คือ"ปิดกั้นโค้ด JavaScript ส่วนหน้าของฉันจากการดูเนื้อหาของส่วนตอบสนองและส่วนหัวในทุกสถานการณ์ ในกรณีส่วนใหญ่เห็นได้ชัดว่าไม่ใช่สิ่งที่คุณต้องการ


เท่าที่เป็นกรณีที่คุณจะต้องการพิจารณาใช้mode: 'no-cors'ให้ดูคำตอบในสิ่งที่ข้อ จำกัด นำไปใช้กับการตอบสนองทึบแสง? สำหรับรายละเอียด ส่วนสำคัญของมันคือกรณีที่:

  • ในกรณีที่ จำกัด เมื่อคุณกำลังใช้งาน JavaScript เพื่อนำเนื้อหาจากแหล่งกำเนิดอื่นเป็น<script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>หรือ<iframe>องค์ประกอบ (ซึ่งทำงานเพราะการฝังตัวของทรัพยากรข้ามจุดที่ได้รับอนุญาตสำหรับผู้) - แต่ด้วยเหตุผลบางอย่างที่คุณ don' ไม่ต้องการหรือไม่สามารถทำได้เพียงแค่ให้มาร์กอัพของเอกสารใช้ URL ของทรัพยากรเป็นhrefหรือsrcแอตทริบิวต์สำหรับองค์ประกอบ

  • เมื่อสิ่งเดียวที่คุณต้องการทำกับทรัพยากรคือการแคช ดังที่กล่าวถึงในคำตอบข้อ จำกัด อะไรที่นำไปใช้กับการตอบสนองแบบทึบแสง? ในทางปฏิบัติสถานการณ์ที่นำไปใช้คือเมื่อคุณกำลังใช้บริการแรงงานซึ่งในกรณีนี้ API ที่เกี่ยวข้องคือการจัดเก็บแคช API

แต่แม้ในกรณีที่ จำกัด เหล่านั้นก็มี gotchas สำคัญที่ต้องระวัง; ดูคำตอบที่ข้อ จำกัด อะไรที่นำไปใช้กับการตอบกลับทึบแสง? สำหรับรายละเอียด


ฉันยังพยายามส่งผ่านวัตถุ { mode: 'opaque'}

ไม่มีmode: 'opaque'โหมดคำขอ - opaqueแทนที่จะเป็นเพียงคุณสมบัติของการตอบสนองและเบราว์เซอร์จะตั้งค่าคุณสมบัติทึบแสงในการตอบสนองจากคำขอที่ส่งมาพร้อมกับno-corsโหมด

แต่โดยบังเอิญคำว่าทึบแสงเป็นสัญญาณที่ชัดเจนเกี่ยวกับธรรมชาติของการตอบสนองที่คุณลงท้ายด้วย:“ ทึบแสง” หมายความว่าคุณไม่สามารถมองเห็นได้


4
ชอบcors-anywhereวิธีแก้ปัญหาสำหรับกรณีใช้งานที่ไม่ใช่การแยงแบบง่าย ๆ (เช่นดึงข้อมูลที่เปิดเผยต่อสาธารณชนบางส่วน) คำตอบนี้ยืนยันความสงสัยของฉันที่no-corsไม่ธรรมดาเพราะ OpaqueResponse ไม่มีประโยชน์มาก เช่น "กรณีที่ จำกัด มาก"; ทุกคนสามารถอธิบายตัวอย่างที่no-corsมีประโยชน์ให้ฉันได้หรือไม่
ถั่วแดง

1
@TheRedPea ดูการอัปเดตที่ฉันทำกับคำตอบที่นี่ (และฉันยังได้เพิ่มความคิดเห็นสำหรับคำถามของคุณที่stackoverflow.com/questions/52569895/ ...... )
sideshowbark

ฉันต้องการกำหนดค่าเซิร์ฟเวอร์ Express ของฉันด้วยแพ็คเกจ CORS.js - github.com/expressjs/cors - และจากนั้นฉันต้องลบออกmode: 'no-cors'จากคำขอการดึงข้อมูล (มิฉะนั้นการตอบสนองจะว่างเปล่า)
James L.

proxy CORS ยอดเยี่ยม ฉันประหลาดใจที่ถือว่าเป็นมาตรการ "ความปลอดภัย" เพื่อบล็อกการเข้าถึงไฟล์สาธารณะเลย มันง่ายมากที่จะหลีกหนีจากมุมมองของแฮ็กเกอร์และนั่นคือสิ่งที่ทำ มันเป็นเรื่องยุ่งยากสำหรับนักแสดงที่ดี
Seph Reed

ฉันสร้างรหัสของไคลเอนต์ต่าง ๆ ที่ใช้ API เดียวกัน ฉันใช้สำหรับฝึกอบรม ฉันต้องการให้รหัสง่ายและให้ผู้ใช้เล่นกับมัน ฉันต้องใช้ไม่มีการรอคอย
profimedica

6

ดังนั้นหากคุณชอบฉันและพัฒนาเว็บไซต์บน localhost ที่คุณพยายามดึงข้อมูลจาก Laravel API และใช้ใน Vue front-end ของคุณและคุณเห็นปัญหานี้นี่คือวิธีที่ฉันแก้ไข:

  1. ในโครงการ Laravel php artisan make:middleware Corsคำสั่งเรียกของคุณ สิ่งนี้จะสร้างapp/Http/Middleware/Cors.phpสำหรับคุณ
  2. เพิ่มรหัสต่อไปนี้ภายในhandlesฟังก์ชั่นในCors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  3. ในapp/Http/kernel.phpเพิ่มรายการต่อไปนี้ใน$routeMiddlewareอาร์เรย์:

    cors => \App\Http\Middleware\Cors::class

    (จะมีรายการอื่น ๆ ในอาร์เรย์เช่นauth, guestฯลฯ นอกจากนี้ให้แน่ใจว่าคุณกำลังทำเช่นนี้ในapp/Http/kernel.phpเพราะมีอีกkernel.phpมากเกินไปใน Laravel)

  4. เพิ่มมิดเดิลแวร์นี้ในการลงทะเบียนเส้นทางสำหรับเส้นทางทั้งหมดที่คุณต้องการอนุญาตการเข้าถึงเช่นนี้

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. ใน Vue front-end ให้แน่ใจว่าคุณเรียก API นี้ในการทำงานและไม่ได้อยู่ในmounted() data()ตรวจสอบให้แน่ใจว่าคุณใช้http://หรือhttps://กับ URL ในการfetch()โทรของคุณ

เครดิตเต็มพีทฮุสตันบทความบล็อก


2

วิธีง่ายๆ:เพิ่มสิ่งต่อไปนี้ลงในไฟล์ php ที่คุณต้องการขอข้อมูล

header("Access-Control-Allow-Origin: *");


8
นี่เป็นความคิดที่ดีมากหากคุณต้องการความปลอดภัยน้อยที่สุด :)
นอกรีตลิง

hotlink เกี่ยวกับภาพ
user889030

1

ทางออกสำหรับฉันคือทำเพียงแค่ฝั่งเซิร์ฟเวอร์

ฉันใช้WebClientไลบรารีC # เพื่อรับข้อมูล (ในกรณีของฉันคือข้อมูลภาพ) และส่งกลับไปยังไคลเอนต์ อาจมีบางสิ่งที่คล้ายกันมากในภาษาฝั่งเซิร์ฟเวอร์ที่คุณเลือก

//Server side, api controller

[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
    ItemImage image = new ItemImage();

    using(WebClient client = new WebClient()){

        image.Bytes = client.DownloadData(url);

        return Ok(image);
    }
}

คุณสามารถปรับแต่งให้เข้ากับกรณีการใช้งานของคุณเอง จุดหลักclient.DownloadData()ทำงานได้โดยไม่มีข้อผิดพลาด CORS โดยทั่วไปปัญหา CORS อยู่ระหว่างเว็บไซต์เท่านั้นดังนั้นจึงไม่เป็นไรที่จะทำการร้องขอ 'ข้ามไซต์' จากเซิร์ฟเวอร์ของคุณ

จากนั้นการเรียกการตอบกลับจะง่ายเหมือน:

//React component

fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
        method: 'GET',
    })
    .then(resp => resp.json() as Promise<ItemImage>)
    .then(imgResponse => {

       // Do more stuff....
    )}

1

วิธีแก้ปัญหาที่ง่ายมาก (2 นาทีเพื่อกำหนดค่า) คือการใช้แพคเกจท้องถิ่น -slslnpm

การใช้งานตรงไปตรงมา:
1. ติดตั้งแพคเกจ: npm install -g local-ssl-proxy
2. ในขณะที่ใช้local-serverหน้ากากของคุณด้วยlocal-ssl-proxy --source 9001 --target 9000

PS:แทนที่--target 9000ด้วย-- "number of your port"และ--source 9001ด้วย--source "number of your port +1"


1
ฉันมีปัญหาในการใช้แอพของฉันในอุปกรณ์โทรศัพท์จริง โซลูชันของคุณมีประโยชน์สำหรับกรณีนี้หรือไม่
ฮามิด Araghi

@HamidAraghi ฉันคิดว่าตอนนี้เนื่องจากเพื่อวัตถุประสงค์ในการพัฒนาพร็อกซีเซิร์ฟเวอร์ควรทำงานบนอุปกรณ์ที่ได้รับผลกระทบจากข้อผิดพลาดจริง ๆ
volna
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.