На запитуваному ресурсі немає заголовка "Access-Control-Allow-Origin" - при спробі отримати дані з API REST


465

Я намагаюся отримати деякі дані з REST API HP Alm. Це досить добре працює з невеликим сценарієм curl - я отримую свої дані.

Зараз це робити з JavaScript, витягненням та ES6 (більш-менш) здається більшою проблемою. Я продовжую отримувати це повідомлення про помилку:

API завантаження не може завантажити. Відповідь на запит перед польотом не проходить перевірку контролю доступу: на запитуваному ресурсі немає заголовка "Access-Control-Allow-Origin". Таким чином, джерело " http://127.0.0.1:3000 " не має доступу. У відповіді був код статусу HTTP 501. Якщо непрозорий відповідь відповідає вашим потребам, встановіть режим запиту на "no-cors", щоб отримати ресурс з вимкненим CORS.

Я розумію, що це тому, що я намагаюся отримати ці дані з мого localhost, і рішення повинно використовувати 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. Я також спробував використовувати цей плагін Chrome CORS, але потім я отримую ще одне повідомлення про помилку:

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

Відповіді:


820

Ця відповідь охоплює багато підстав, тому її поділяють на три частини:

  • Як використовувати CORS проксі , щоб обійти «No Access-Control-Allow-Origin заголовок» проблеми
  • Як уникнути передпольоту CORS
  • Як виправити проблеми з заголовком "Access-Control-Allow-Origin" не повинно бути підстановкою "

Як використовувати CORS проксі , щоб обійти «No Access-Control-Allow-Origin заголовок» проблеми

Якщо ви не керуєте сервером, на який ви відправляєте запит на код frontend 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/ . Тож замість того https://cors-anywhere.herokuapp.com, щоб префіксувати URL-адресу вашого запиту за допомогою , префікс, а не 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 - тому що в цьому випадку proxy також надсилає назад Access-Control-Allow-Headersі Access-Control-Allow-Methodsзаголовки, необхідні для успішного передпольоту.


Як уникнути передпольоту CORS

Код у запитанні запускає попередній політ CORS - оскільки він надсилає Authorizationзаголовок.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Prefliisted_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_inURL-адресу.

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

Таким чином, ви ніколи не зможете робити 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заголовок відповіді є *. Замість того, щоб значення в цьому випадку повинен точно відповідати походження вашого FRONTEND кодексу, в http://127.0.0.1:3000.

Див. Запити та посвідчення підтверджених даних у статті контролю доступу HTTP MDN (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-*заголовки відповіді заголовки. Ніколи не хочеш надсилати їх у запиті. Єдиний ефект, який потрібно мати, - це запустити веб-переглядач перед полетом.


1
Тож URL-адреса виглядала б так: 127.0.0.1:9999/127.0.0.1.1000000 з корками, правда?
daniel.lozynski

Якщо ви використовуєте власний примірник коду github.com/Rob--W/cors-anywhere за адресою 127.0.0.1:9999, і ви хочете зробити запит на 127.0.0.1.1000000 , так
Sidehowbarker

4
Чудова відповідь, моя проблема полягала в тому, що віддалений сервер не відповідав на запити OPTIONS, тож, поспілкувавшись із запитами та заголовками за те, що здавалося віком, я вирішив це, видаливши заголовки Content-Typeі Access-Control-Allow-Origin- дякую!
Морваел

1
Мені не подобається ідея використання проксі-сервера, якщо ви не керуєте нею, інакше ви отримуєте дані через ненадійну третю сторону, і вона відкрита для фальсифікації тощо.
DaveMongoose

1
Це поглиблена і чудова відповідь, дякую. Що я особисто робив під час тестування, це Відкритий хром із прапором безпеки відключення веб-сторінок, і він працював. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Карун

101

Ця помилка виникає, коли URL-адреса клієнта та URL-адреса сервера не збігаються, включаючи номер порту. У цьому випадку вам потрібно включити вашу послугу для CORS, яка є спільним джерелом спільного використання ресурсів.

Якщо ви розміщуєте послугу Spring REST, то ви можете знайти її у службі підтримки блогу CORS у Spring Framework .

Якщо ви розміщуєте послугу за допомогою сервера Node.js, тоді

  1. Зупиніть сервер Node.js.
  2. npm install cors --save
  3. Додайте наступні рядки до свого server.js

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

4
Працював без жодних питань ... дуже корисно з джерелом посилання на ресурси .. Дякую велике
Rajesh Mbm

4
including the port number:(
Скоттісей

1
Дійсно корисний, що я зробив мій день
кунальний товариш

номер порту врятував моє життя
Ашад

Найпростіша відповідь колись дякую :)
стеки

46

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

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

Ці заголовки належать до відповіді , а не до запиту. Тому видаліть їх, включаючи рядок:

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

У вашому запиті було вказано "Тип вмісту: 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));
}

4
Це має бути головна відповідь - мені дуже не подобається ідея обійти CORS, особливо шляхом його маршрутизації через третю сторону.
DaveMongoose

ей, що "Header ()" будь ласка?
міцу

@mitsu Якщо ви посилаєтесь на рядок: нехай заголовки = нові заголовки (); вище, тоді це інтерфейс API отримання для виконання дій із заголовками запиту або заголовками відповідей. Відвідайте developer.mozilla.org/en-US/docs/Web/API/Headers, щоб отримати детальну інформацію та приклади їх використання.
Lex Soft

1
Чітка і точна відповідь .. її логічно і точно вирішує питання. - Thx
Dhammika

7

У моєму випадку я використовую наведене нижче рішення

Передній або кутовий

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

бек-енд (я використовую 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);

2

Тільки два мої центи ... щодо використання проксі-сервера CORS для вирішення проблем "Без Access-Control-Allow-Originзаголовка"

Для тих з вас, хто працює з php в бекенде, розгортання "проксі-сервера CORS" є простим, як:

  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/*})



1

Використання dataType: 'jsonp'працювало для мене.

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });


 } // function               

1

У моєму випадку веб-сервер перешкоджав методу "OPTIONS"

Перевірте свій веб-сервер щодо методу параметрів

Я використовую "webtier"

/www/webtier/domains/ evidencedomainnameSense/config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

перейти

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

0

Я працював з Spring REST, і вирішив це, додавши AllowedMethods у WebMvcConfigurer.

@Value( "${app.allow.origins}" )
    private String allowOrigins;    
@Bean
public WebMvcConfigurer corsConfigurer() {
            System.out.println("allow origin: "+allowOrigins);
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                    //.allowedOrigins("http://localhost")
                    .allowedOrigins(allowOrigins)
                    .allowedMethods("PUT", "DELETE","GET", "POST");
                }
            };
        }

-1

Я отримував цю помилку на моїй локальній. Я керував Visual Studio як адміністратор, і це вирішено.

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