Этот ответ охватывает много вопросов, поэтому он разделен на три части:
- Как использовать прокси-сервер 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-адресу прокси - вызывает сделать запрос через тот прокси, который затем:
- Направляет запрос на
https://example.com
.
- Получает ответ от
https://example.com
.
- Добавляет
Access-Control-Allow-Origin
заголовок к ответу.
- Передает этот ответ с добавленным заголовком обратно в запрашивающий код внешнего интерфейса.
Затем браузер позволяет коду внешнего интерфейса получить доступ к ответу, потому что 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 или другой открытый прокси-сервер, это решение работает, даже если это запрос, который запускает браузер для выполнения OPTIONS
запроса предварительной проверки CORS, потому что в этом случае прокси также отправляет обратно 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
которые должны содержаться Authorization
и Content-Type: application/json
заголовки.
Это работает очень хорошо с небольшим скриптом curl - я получаю свои данные.
Чтобы правильно 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
заменой на любой фактический sign_in
URL.
Ответ, который браузер должен видеть на этот 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
заголовка ответа. Например, с помощью nginx:
add_header Access-Control-Allow-Origin $http_origin
Но это только один пример; другие (веб) серверные системы предоставляют аналогичные способы отображения исходных значений.
Я использую Chrome. Я также пытался использовать этот плагин Chrome CORS
Этот плагин 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-*
заголовки ответа заголовки. Вы никогда не хотите отправить их в запросе. Единственный эффект, который будет иметь место, - это запустить браузер для предварительной проверки.