Попытка использовать fetch и перейти в режим: no-cors


167

Я могу поразить эту конечную точку http://catfacts-api.appspot.com/api/facts?number=99через Почтальон, и она возвращаетсяJSON

Кроме того, я использую create-реагировать-приложение и хотел бы избежать настройки любого сервера конфигурации.

В моем клиентском коде я пытаюсь использовать 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'(фактически это гарантирует, что ваш код веб-интерфейса не сможет получить доступ к содержимому ответа).

Тем не менее, одна вещь, которая будет работать: если вы отправите запрос через прокси-сервер 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/ . Таким образом, вместо того, чтобы добавлять префикс 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 объясняет, почему даже если вы можете получить доступ к ответу с помощью Postman, браузеры не позволят вам получить доступ к перекрестному источнику ответа из внешнего интерфейса Код JavaScript, выполняемый в веб-приложении, если ответ не содержит Access-Control-Allow-Originзаголовок ответа.

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

Ваш браузер может получить хороший ответ, и вы можете увидеть его в Postman и даже в devtools для браузера, но это не значит, что браузеры предоставят его вашему коду. Они не будут, потому что у него нет Access-Control-Allow-Originзаголовка ответа. Поэтому вы должны вместо этого использовать прокси, чтобы получить его.

Прокси-сервер выполняет запрос к этому сайту, получает ответ, добавляет Access-Control-Allow-Originзаголовок ответа и любые другие необходимые заголовки CORS, а затем передает его обратно в ваш запрашивающий код. И этот ответ с Access-Control-Allow-Originдобавленным заголовком - это то, что видит браузер, поэтому браузер позволяет вашему внешнему коду фактически получить доступ к ответу.


Поэтому я пытаюсь передать объект, в мой выбор, который отключит 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 ресурса в качестве атрибута hrefor srcдля элемента.

  • Когда единственное, что вы хотите сделать с ресурсом, это кэшировать его. Как указано в ответе Какие ограничения применяются к непрозрачным ответам? на практике сценарий, который применяется к этому случаю, - это когда вы используете Service Workers, и в этом случае соответствующим API является Cache Storage 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, где вы пытаетесь получить данные из Laravel API и использовать их в своем интерфейсе Vue, и вы видите эту проблему, вот как я ее решил:

  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 fetch так же прост:

//React component

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

       // Do more stuff....
    )}

1

Очень простое решение (2 минуты для настройки) - использовать пакет local-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 я утро бы предположить , что в настоящее время, так как для развивающихся целей прокси - сервер должен запущенным на устройстве, которое на самом деле влияют на ошибки
Волна
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.