Спроба використовувати вибір та перехід у режимі: no-cors


167

Я можу потрапити на цю кінцеву точку http://catfacts-api.appspot.com/api/facts?number=99через Postman, і вона повернетьсяJSON

Крім того, я використовую create-react-додаток і хочу уникати налаштування будь-якого конфігурації сервера.

У своєму клієнтському коді я намагаюся використовувати 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 на передній панелі, щоб за будь-яких обставин переглядати вміст тіла відповіді та заголовків". Звичайно, цього майже ніколи не хочеш.

Що трапляється з запитами на поперечне походження від frontend JavaScript, це те, що браузери за замовчуванням блокують код фронтального доступу до доступу до ресурсів перехресного походження. Якщо Access-Control-Allow-Originу відповіді є, браузери скасують це блокування та дозволять вашому коду отримати доступ до відповіді.

Але якщо сайт не надсилає Access-Control-Allow-Originвідповідей у ​​відповідь, ваш код фронталу не може отримати прямий доступ до відповідей з цього сайту. Зокрема, ви не можете його виправити, вказавши mode: 'no-cors'(адже це забезпечить код вашого фронтального доступу не мати доступу до вмісту відповіді).

Тим НЕ менше, одна річ , яка буде працювати: якщо ви посилаєте запит через проксі - сервер 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, ви виявите, що це знищено , ви також можете легко розгорнути власний проксі на 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

Після запуску цих команд у вас з’явиться власний сервер CORS Anywhere, на якому працює, наприклад, https://cryptic-headland-94862.herokuapp.com/ . Тож, замість того https://cors-anywhere.herokuapp.com, щоб префіксувати URL-адресу запиту за допомогою , префікс замість нього, використовуючи 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 пояснює, чому так буває, що навіть якщо ви можете отримати доступ до відповіді за допомогою Postman, браузери не дозволять вам отримати відповідь перехресного походження з фронтену JavaScript-код, який працює у веб-додатку, якщо відповідь не містить Access-Control-Allow-Originзаголовка відповіді.

http://catfacts-api.appspot.com/api/facts?number=99 не має Access-Control-Allow-Originзаголовка відповіді, тому немає можливості вашому коду фронтального доступу отримати доступ до відповіді перехресного походження.

Ваш веб-переглядач може отримати штраф відповіді, і ви зможете побачити його у Postman та навіть у розробниках браузера, але це не означає, що браузери піддають його коду. Вони не стануть, тому що в ньому немає 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>елемент (який працює тому , що вкладення коштів у поперечному походження допускається для тих , хто) - але з якоїсь - то причини ви не » не хочу або не можу цього зробити, лише розмітивши документ, використовуйте URL-адресу ресурсу як елемент hrefабо srcатрибут для елемента.

  • Коли єдине, що ви хочете зробити з ресурсом, - це кешувати його. Як згадується у відповіді Які обмеження застосовуються до непрозорих відповідей? на практиці застосовується сценарій, коли ви використовуєте службових службовців, і в цьому випадку API, що є релевантним, є API зберігання кешу .

Але навіть у цих обмежених випадках слід пам’ятати про деякі важливі проблеми. див. відповідь у розділі Які обмеження застосовуються до непрозорих відповідей? для деталей.


Я також намагався пройти в об’єкт { mode: 'opaque'}

Немає mode: 'opaque'режиму запиту - opaqueце натомість просто властивість відповіді , і браузери встановлюють це непрозоре властивість для відповідей на запити, надіслані з no-corsрежимом.

Але, до речі, слово непрозорий є досить явним сигналом про характер відповіді, яку ви закінчуєте: "непрозорий" означає, що ви не можете його бачити.


4
Подобається cors-anywhereвирішити прості випадки, які не використовуються (тобто отримання деяких загальнодоступних даних). Ця відповідь підтверджує мою підозру, яка no-corsне є загальною, тому що OpaqueResponse не дуже корисний; тобто "дуже обмежені випадки"; хтось може мені пояснити приклади, де no-corsце корисно?
Червоний горох

1
@TheRedPea Дивіться оновлення, яке я зробив до відповіді тут (і що я також додав як коментар до вашого питання на сайті stackoverflow.com/questions/52569895/… )
sideshowbarker

Мені потрібно було налаштувати мій сервер Express за допомогою пакета CORS.js - github.com/expressjs/cors - і тоді мені потрібно було видалитиmode: 'no-cors' запит на отримання (інакше відповідь буде порожнім)
Джеймс Л.

проксі-сервер CORS чудовий. Я вражений, що це вважається заходом "безпеки" взагалі блокувати доступ до публічних файлів. Це так, так просто обійтись з точки зору хакера, і саме це і робить. Це справді просто клопот з хорошими акторами.
Сеф Рід

Я генерую код різних клієнтів, які використовують один і той же API. Я використовую це для тренувань. Я хочу зберегти простий код і дозволити користувачам грати з ним. Мені потрібно скористатися безкультурками.
profimedica

6

Тож якщо ви схожі на мене і розробляєте веб-сайт у localhost, де ви намагаєтеся отримати дані з API Laravel і використовувати їх у вашому 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()дзвінку

Повні кредити до статті блогу Піта Х'юстона .



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 простий як:

//React component

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

       // Do more stuff....
    )}

1

Дуже простим рішенням (2 хвилини до конфігурації) є використання локального пакета ssl-proxy відnpm

Використання прямо вперед:
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
У мене є проблема з використанням свого додатка в реальному телефоні. Чи корисне ваше рішення у цій справі?
Хамід Арагі

@HamidAraghi Я вважаю, що зараз, оскільки для розвиваючих цілей проксі-сервер повинен працювати на пристрої, на який насправді впливає помилка
volna
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.