Как устранить ошибку запроса Access-Control-Allow-Origin на стороне клиента с помощью собственного API Symfony 3

Эта ошибка будет обнаружена и сообщена на стороне клиента, когда кто-то запрашивает с помощью Javascript (AJAX) конечную точку вашего проекта Symfony, который обычно (но не обязательно) является API. В большинстве случаев эта ошибка не может быть решена на стороне клиента, так как ошибка фактически вызвана сервером, который, в свою очередь, является не ошибкой, а «мерой безопасности».

Эта мера безопасности является политикой Same-Origin, эта политика устанавливает, что веб-браузер разрешает скрипты, содержащиеся на первой веб-странице (www.myweb.com/page1.html) для доступа к данным на второй веб-странице (www.myweb.com/script.js), но только если обе веб-страницы имеют одинаковое происхождение. Источник определяется как комбинация схемы URI (http:// или же https:// etc), имя хоста (www.domain.com) и номер порта (обычно порт 80). Короче говоря, это означает, что создать запрос к website A нам нужно отправить его с того же website A, если вы делаете это из website B тогда политика будет применена, и вы найдете ошибку в консоли.

Эта политика была в некотором роде излишней, потому что, что, если вашему проекту нужно поделиться некоторой информацией со сторонними сайтами? Чтобы решить эту проблему, мы используем спецификацию CORS на нашем сервере. Распределение ресурсов между источниками (CORS) — это спецификация, которая обеспечивает действительно открытый доступ через границы домена. Так что если вы публикуете публичный контент, вам нужно учитывать (каким-то образом … вам нужно) используя CORS, чтобы открыть его для универсального доступа JavaScript / браузера. Вы можете прочитать больше о CORS здесь.

Если вы выполняете запрос XMLHttpRequest из браузера до конечной точки вашего приложения (https://sandbox/api) с Javascript с другого сайта (https://fiddle.jshell.net) используя следующий код:

$.getJSON("https://sandbox/api", function(data){
console.log(data);
});

В консоли вы получите следующее сообщение об ошибке:

XMLHttpRequest не может загрузить https: // песочница / апи. В запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Происхождениеhttps://fiddle.jshell.netПоэтому не допускается доступ.

Как правило, в PHP вы можете включить CORS в своем скрипте, реализовав следующий заголовок:

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

Однако, поскольку вы используете Symfony, вы не будете этого делать. Вместо этого вам нужно изменить возвращенный ответ в контроллере.

Решите с ответами в контроллерах

Используя модель контроллера, в этом примере мы собираемся использовать простой контроллер, который генерирует ошибку (не имеет заголовков) и возвращает простой ответ JSON:

setContent(json_encode([
'id' => uniqid(),
'time' => $date->format("Y-m-d")
]));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}

Чтобы решить эту проблему, нам нужно изменить ответ и добавить Access-Control-Allow-Origin заголовок:

setContent(json_encode([
'id' => uniqid(),
'time' => $date->format("Y-m-d")
]));
$response->headers->set('Content-Type', 'application/json');
// Allow all websites
$response->headers->set('Access-Control-Allow-Origin', '*');
// Or a predefined website
//$response->headers->set('Access-Control-Allow-Origin', 'https://jsfiddle.net/');
// You can set the allowed methods too, if you want
//$response->headers->set('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, PATCH, OPTIONS');
return $response;
}

Параметр origin указывает URI, который может обращаться к ресурсу. Браузер должен обеспечить это. Для запросов без учетных данных сервер может указать*"подстановочный знак, что позволяет любому источнику получить доступ к ресурсу.

Решить со статическими файлами и уже реализованным API

Но что, если вместо этого вы обрабатываете статические файлы или у вас уже есть огромный встроенный API? Например:

1) с файлами: если у вас есть файл (myfile.txt) в веб-каталоге (в папке ресурсов) вашего проекта Symfony (в домен А) и вы хотите запросить этот файл у домен B с AJAX:

$.get("https://sandbox/resources/myfile.txt", function(data){
console.log(data);
});

2) С уже встроенным API: давайте представим, что вы уже создали Restful API, используя FOSRestBundle:

В обоих случаях вы найдете одну и ту же ошибку «XMLHttpRequest не может загрузить» в консоли, поэтому вам нужно будет добавлять упомянутый заголовок в каждый ответ. Однако изменение ответа в каждом контроллере или даже возврат файлов с чистым PHP вместо ngix будет контрпродуктивным и очень неэффективным. Поэтому, чтобы сделать это правильным и простым способом, мы будем зависеть от NelmioCorsBundle. NelmioCorsBundle позволяет отправлять заголовки общего доступа к ресурсам между источниками с настройкой каждого URL-адреса в стиле ACL.

Чтобы установить NelmioCorsBundle, выполните следующую команду в composer:

composer require nelmio/cors-bundle

Или добавьте следующую строку в ваш composer.json файл и затем выполнить composer install:

{
"require": {
"nelmio/cors-bundle": "^1.4"
}
}

Затем перейдите к регистрации пакета в файле AppKernel (app/AppKernel.php) в методе registerBundles:

И наконец приступаем к настройке необходимой конфигурации, чтобы ваш проект работал (Узнайте больше о NelmioCorsBundle в официальном репозитории в Github здесь).

В соответствии с вашими потребностями и требованиями вашего проекта, вам может потребоваться прочитать документацию пакета, чтобы увидеть, какие опции вам нужно включить и изменить. Тем не менее, следующая конфигурация в config.yml файл должен сделать трюк, чтобы сделать /api конечная точка (и все под-URL [api/something, api/other-endpoint]) доступны для доступа из других доменов:

nelmio_cors:
defaults:
allow_credentials: false
allow_origin: []
allow_headers: []
allow_methods: []
expose_headers: []
max_age: 0
hosts: []
origin_regex: false
paths:
'^/api':
allow_origin: ['*']
allow_headers: ['*']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600

/api К конечной точке можно получить доступ из любых доменов и разрешить любой тип заголовка, вы можете захотеть отфильтровать это в своем проекте. Не забудьте очистить кеш перед тестированием, и вы готовы к работе!

Решите это на стороне клиента

Если вы получаете эту ошибку, когда пытаетесь получить доступ к стороннему API (и они, вероятно, не решат ее в течение некоторого времени), вы можете положиться на неортодоксальный метод для извлечения данных из API с помощью Javascript, легко используя Cors-везде бесплатный сервис.

Ссылка на основную публикацию
Adblock
detector