На запрашиваемом ресурсе нет заголовка 'Access-Control-Alloww-Origin' при попытке получить данные из REST API.

javascript api cors fetch-api preflight


Я пытаюсь получить некоторые данные из REST API HP Alm.Это довольно хорошо работает с небольшим скриптом скручивания-я получаю свои данные.

Теперь делать это с JavaScript,fetch и ES6 (более или менее)кажется более серьезной проблемой.Я продолжаю получать это сообщение об ошибке:

Fetch API не может загрузить. Ответ на запрос предварительной проверки не проходит проверку контроля доступа: в запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Поэтому источнику " http://127.0.0.1:3000 " запрещен доступ. Ответ имеет HTTP-код состояния 501. Если непрозрачный ответ удовлетворяет вашим потребностям, установите режим запроса «no-cors», чтобы получить ресурс с отключенным CORS.

Я понимаю,что это происходит потому,что я пытаюсь получить данные из своего локального хоста,а решение должно использовать CORS.Теперь я думал,что на самом деле сделал это,но каким-то образом это либо игнорирует то,что я пишу в заголовке,либо проблема в чем-то другом?

Итак,есть проблема с внедрением? Я делаю это неправильно? К сожалению,я не могу проверить логи сервера.Я действительно немного застрял здесь.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

Я использую Хром.Я также пытался использовать Chrome CORS Plugin,но потом получил еще одно сообщение об ошибке:

Значение заголовка «Access-Control-Allow-Origin» в ответе не должно быть подстановочным знаком «*», если режим учетных данных запроса «включить». Поэтому источнику " http://127.0.0.1:3000 " запрещен доступ. Режим учетных данных запросов, инициируемых XMLHttpRequest, контролируется атрибутом withCredentials.




Answer 1 sideshowbarker


Этот ответ охватывает много вопросов, поэтому он разделен на три части:

  • Как использовать прокси-сервер CORS для решения проблемы «Нет заголовка Access-Control-Allow-Origin»
  • Как избежать предполета CORS
  • Как исправить «заголовок Access-Control-Allow-Origin не должен быть шаблонными» проблемами

Как использовать прокси-сервер CORS для решения проблемы «Нет заголовка Access-Control-Allow-Origin»

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

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Причина, по которой блок catch срабатывает, заключается в том, что браузер предотвращает доступ кода к ответу, полученному с https://example.com . И причина, по которой браузер делает это, заключается в том, что в ответе отсутствует заголовок ответа Access-Control-Allow-Origin .

Теперь, вот точно такой же пример, но только с добавленным прокси-сервером CORS:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Примечание. Если https://cors-anywhere.herokuapp.com не работает или недоступен при попытке его установки, ознакомьтесь с инструкциями ниже, как установить собственный сервер CORS Anywhere на Heroku всего за 2-3 минуты.

Второй фрагмент кода, приведенный выше, может успешно получить доступ к ответу, потому что взятие URL-адреса запроса и его изменение на https://cors-anywhere.herokuapp.com/https://example.com - путем простого добавления префикса к URL-адресу прокси-сервера - вызывает сделать запрос через тот прокси, который затем:

  1. Перенаправляет запрос на https://example.com .
  2. Получает ответ от https://example.com .
  3. Добавляет заголовок Access-Control-Allow-Origin к ответу.
  4. Передает этот ответ с добавленным заголовком обратно в запрашиваемый код фронтенда.

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

Вы можете легко запустить свой собственный прокси, используя код с https://github.com/Rob--W/cors-anywhere/ .
Вы также можете легко развернуть свой собственный прокси на 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 .

Так что, если вы попытаетесь использовать https://cors-anywhere.herokuapp.com, вы обнаружите, что он не работает (что иногда бывает), а затем подумайте о получении учетной записи Heroku (если вы этого еще не сделали) и возьмите 2 или 3 минуты, чтобы выполнить указанные выше действия для развертывания собственного сервера CORS Anywhere на Heroku.

Независимо от того , управляете ли вы самостоятельно или использовать https://cors-anywhere.herokuapp.com или другие открытые прокси, это решение работает , даже если запрос один , который запускает браузер , чтобы сделать CORS предполетной OPTIONS запрашивает-потому , что в этом случае, прокси-сервер также отправляет обратно Access-Control-Allow-Headers и Access-Control-Allow-Methods , необходимые для успешной предварительной проверки.


Как избежать предполетной проверки CORS

Код в вопросе запускает предварительную проверку CORS, поскольку отправляет заголовок Authorization .

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

Даже без этого заголовок Content-Type: application/json также запускает предпечатную проверку.

Что означает «предварительная проверка»: прежде чем браузер попытается POST в коде, о котором идет речь, он сначала отправит серверу запрос OPTIONS , чтобы определить, подключается ли сервер к получению POST с несколькими источниками, который включает Content-Type: application/json Authorization и тип контента: заголовки приложения / json .

Он довольно хорошо работает с небольшим скриптом скручивания-я получаю свои данные.

Чтобы правильно выполнить тестирование с помощью curl , вы должны эмулировать предварительный запрос OPTIONS ,который отправляет браузер:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

… С https://the.sign_in.url , замененным тем, какой у вас фактический URL для sign_in .

Ответ, который браузер должен видеть из этого запроса OPTIONS , должен включать заголовки, подобные этому:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

Если ответ OPTIONS не включает эти заголовки, браузер сразу же остановится и даже не попытается отправить запрос POST . Кроме того, код состояния HTTP для ответа должен быть 2xx - обычно 200 или 204. Если это любой другой код состояния, браузер остановится прямо здесь.

Сервер в вопросе отвечает на запрос OPTIONS с кодом состояния 501, что, очевидно, означает, что он пытается указать, что не реализует поддержку запросов OPTIONS . В этом случае другие серверы обычно отвечают кодом состояния 405 «Метод не разрешен».

Таким образом, вы никогда не сможете отправлять POST - запросы напрямую на этот сервер из своего внешнего кода JavaScript, если сервер отвечает на этот запрос OPTIONS с помощью 405 или 501 или чего-либо, кроме 200 или 204, или если не отвечает с помощью эти необходимые заголовки ответа.

Путь к тому,чтобы не инициировать предполетный полет по данному делу,будет заключаться в следующем:

  • если серверу не требовался заголовок запроса Authorization а вместо этого (например) использовались данные аутентификации, встроенные в тело запроса POST или в качестве параметра запроса
  • если серверу не требуется, чтобы тело POST имело медиа-тип Content-Type: application/json а вместо этого приняло тело POST как application/x-www-form-urlencoded с параметром с именем json (или любым другим), значение которого равно данные JSON

Как исправить «заголовок Access-Control-Allow-Origin не должен быть шаблонными» проблемами

Я получаю еще одно сообщение об ошибке:

Значение заголовка «Access-Control-Allow-Origin» в ответе не должно быть подстановочным знаком «*», если режим учетных данных запроса «включить». Поэтому источнику " http://127.0.0.1:3000 " запрещен доступ. Режим учетных данных запросов, инициируемых XMLHttpRequest, контролируется атрибутом withCredentials.

Для запроса, содержащего учетные данные, браузеры не разрешают вашему внешнему JavaScript-коду обращаться к ответу, если значение заголовка ответа Access-Control-Allow-Origin равно * . Вместо этого значение в этом случае должно точно соответствовать происхождению кода вашего внешнего интерфейса, http://127.0.0.1:3000 .

См. Учетные запросы и подстановочные знаки в статье MDN HTTP control access (CORS).

Если вы управляете сервером, на который отправляете запрос, то обычным способом решения этого случая является настройка сервера на получение значения заголовка запроса Origin и его отображение / отображение обратно в значении Access-Control-Allow-Origin Заголовок ответа Control-Allow-Origin . Например, с помощью nginx:

add_header Access-Control-Allow-Origin $http_origin

Но это только один пример; другие (веб) серверные системы предоставляют аналогичные способы отображения исходных значений.


Я использую Хром.Я также попробовал использовать Chrome CORS Plugin.

Этот плагин Chrome CORS, очевидно, просто вводит заголовок Access-Control-Allow-Origin: * в ответ, который видит браузер. Если бы плагин был умнее, то он установил бы значение этого поддельного заголовка ответа Access-Control-Allow-Origin к фактическому источнику вашего внешнего кода JavaScript, http://127.0.0.1:3000 .

Поэтому не используйте этот плагин даже для тестирования. Это просто отвлечение. Если вы хотите проверить, какие ответы вы получаете с сервера, а браузер их не фильтрует, лучше использовать curl -H , как указано выше.


Что касается внешнего кода JavaScript для запроса fetch(…) в вопросе:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Удалить эти строки. Access-Control-Allow-* заголовки ответа заголовки. Вы никогда не хотите отправить их в запросе. Единственный эффект, который будет иметь место, - это запустить браузер для предварительной проверки.




Answer 2 Rakesh


Эта ошибка возникает,когда URL клиента и URL сервера не совпадают,включая номер порта.В этом случае вам необходимо включить ваш сервис для CORS,который является межсетевым распределением ресурсов.

Если у вас есть служба Spring REST, вы можете найти ее в блоге поддержки CORS в Spring Framework .

Если вы предоставляете услугу с использованием сервера Node.js,тогда

  1. Остановите сервер Node.js.
  2. npm install cors --save
  3. Добавьте следующие строки к вашему серверу.js

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration



Answer 3 Lex Soft


Проблема возникла потому, что вы добавили следующий код в качестве заголовка запроса в ваш интерфейс:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Эти заголовки принадлежат ответу , а не запросу. Поэтому удалите их, включая строку:

headers.append('GET', 'POST', 'OPTIONS');

Ваш запрос содержал «Content-Type: application / json», следовательно, вызвал так называемый предварительный просмотр CORS. Это привело к тому, что браузер отправил запрос методом OPTIONS. См. Предварительную проверку CORS для получения подробной информации.
Поэтому в вашем бэкэнде вы должны обработать этот предварительно выданный запрос, возвращая заголовки ответа, которые включают:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

Конечно,фактический синтаксис зависит от языка программирования,который вы используете для вашего бэк-энда.

В вашем фронте это должно быть так:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}



Answer 4 Harrison O


В моем случае,я использую следующее решение

Передний или угловой

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

back-end (я использую php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);



Answer 5 Yair Levy


Только мои два цента ... о том, как использовать прокси-сервер CORS, чтобы обойти проблемы «Нет заголовка Access-Control-Allow-Origin »

Для тех из вас,кто работает с php на бэкэнде,установка "CORS прокси" так же проста,как и установка "CORS proxy":

  1. создать файл под названием 'no-cors.php' со следующим содержимым:

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. на передней стороне,сделай что-нибудь вроде:

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})