AJAX-запросы между доменами
Открыл сервис предпросмотра Markdown, порадовался за себя, но не заметил одной ошибки. Точнее, заметил, но не сразу. Через обычный POST всё шло славненько, а вот AJAX-запросы с других доменов не работали. Здесь я расскажу как сделать AJAX-сервис на вашем сайте действительно публичным.
Погуглив, нашёл ряд статеек на тему кроссдоменных запросов. Один из способов - это создание прокси на родном сервере и далее Curl'ом передавать данные и возвращать ответ. Не вариант, если уж я решил сделать публичный сервис. Нашёл интересные хаки, но опять же, не для моих нужд. И наконец наткнулся на термин CORS.
В прекрасном, но чересчур подробном указании от W3C я долго не мог понять о каких заголовках идёт речь и много времени потерял, увлёкшись, видимо, процессом поиска, а не чтения. Чуть позже, разобравшись что ищу, я вернулся и докурил этот в целом полезный, мануал. Больше всего понравилась статейка от developer.mozilla.org которая без лишней воды описывает реализацию в двух вариантах - Simple и Preflighted. К слову, у меня заработал только Preflighted, уж не знаю чем простой не угодил. И так, в чём он заключается.
Делая первый запрос к серверу, клиент сначала спрашивает разрешения посредством OPTIONS-запроса. В ответ, сервер должен заголовками сигнализировать о своей открытости для общения. Для этого понадобится взять пару заголовков из запроса и вставить их в заголовки ответа. На PHP это выглядит так:
$headers = apache_request_headers();
$headers = array_change_key_case($headers, CASE_LOWER); // чтобы не было непоняток и разночтений
header('Access-Control-Allow-Origin', @$headers['origin']); // разрешаем хост который указан Origin
header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS'); // нужное подчеркнуть или оставить как есть
header('Access-Control-Allow-Headers', @$headers['access-control-request-headers']); // это должно быть как в запросе
header('Access-Control-Max-Age', '600'); // время в секундах
Или вот вариант для Symfony, вставляем в нужный контроллер, например apps/frontend/modules/test/actions/actions.class.php:
$this->getResponse()->setHttpHeader('Access-Control-Allow-Origin', $request->getHttpHeader('origin') );
$this->getResponse()->setHttpHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
$this->getResponse()->setHttpHeader('Access-Control-Allow-Headers', $request->getHttpHeader('access-control-request-headers') );
$this->getResponse()->setHttpHeader('Access-Control-Max-Age', '600');
Клиент получит от сервера эти заголовки, запомнит что ему разрешено делать POST-запросы и совершит сие действо. Данный механизм реализован в популярных браузерах и менять в своих javascript ничего не придётся. Для теста советую закомментировать последнюю строчку, чтобы отлавливать OPTIONS-запросы не раз в 10 минут, а на каждом запросе.
Рад, если статья помогла сэкономить кому-то пару часов времени.
