Я работаю с CodeIgniter и Restfull API, чтобы структурировать собственный API веб-сервера.
Я начал использовать CORS в соответствии с требованиями для некоторых фреймворков, которые я использую.
Работая с JQuery, я вижу, что отправлено 2 запроса, сначала как тип OPTION – как и ожидалось, – но без моего настраиваемого заголовка (X-API-KEY для безопасности по умолчанию в CodeIgniter Restful API).
Затем я получаю неверное сообщение об ошибке API-ключа, как показано на рисунке. Затем сразу после отправки правильного запроса с правильными заголовками, но в то же время первые запросы инициировали функцию .fail () для обработки ошибок.
Какова наилучшая практика для обработки этого сценария? Я бы хотел, чтобы мой запрос ajax плавно обрабатывал первый запрос предваротельного запроса OPTION без запуска ошибки в моем приложении, как это происходит сегодня, а затем выполняет обычный вызов GET с настраиваемыми заголовками в соответствии с тем, как работает CORS и выполняет успешный вызов, никогда не вызывая ошибку вызов в первом предполетном запросе?
triggerFriendsWall: function() { //Get location var options = { timeout: 30000, enableHighAccuracy: true, maximumAge: 90000 }; //We need to check if user has disabled geolocation, in which case it makes the app crashes ! (from Cordova.js 1097) var position = JSON.parse(localStorage.getItem("position")); if (position == "" || position == null || position == "null" || typeof position == "undefined" ) { // In this case we have never set location to anything and the user has enabled it. navigator.geolocation.getCurrentPosition( function(position) { home.friendsWall(position); }, function(error) { common.handle_errors(error, home.friendsWall); }, options); } else { // in this case, user has disabled geolocatoin ! common.handle_errors(false, home.friendsWall); } }, friendsWall: function(position) { $.when(UserDAO.getUsersNearby(position.coords.latitude, position.coords.longitude, home.Usr_radius, home.Usr_limit, home.Usr_offset)) .done(function(response) { // Do stuff }) } getUsersNearby: function(lat, lng, radius, limit, offset) { var key = localStorage.getItem("key"); return $.ajax({ type: "GET", url: config.server_url + 'user/usersSurrounding', headers: { 'X-API-KEY': key }, data: { lat: lat, lng: lng, radius: radius, limit: limit, offset: offset }, dataType: 'json' }); },
Большое спасибо
EDIT: это конструктор, связанный со всеми моими контроллерами (весь контроллер расширяет один контроллер, где метод построения:)
public function __construct() { header('Access-Control-Allow-Origin: *'); header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method"); header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE"); $method = $_SERVER['REQUEST_METHOD']; if($method == "OPTIONS") { die(); } parent::__construct(); // $this->load->model('admin_model'); $this->load->library('session'); $this->load->library('key'); }
Используете ли вы Access-Control-Allow-Headers ?
Используется в ответ на запрос перед полетом, чтобы указать, какие заголовки HTTP могут использоваться при выполнении фактического запроса.
Попробуйте добавить следующий заголовок в свой предполетный код.
header("Access-Control-Allow-Headers: content-type, origin, accept, X-API-KEY");
Я помню, что у меня были похожие проблемы, похоже, что некоторые из них также являются специфичными для браузера …
Если это помогает здесь, это фрагмент кода, который я знаю, работает:
// CORS and other headers. Make sure file is not cached (as it happens for example on iOS devices) header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: " . gmdate("D, d MYH:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST'); header('Access-Control-Max-Age: ' . CORS_AUTH_MAX_AGE); //CORS preflight if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') { header("Access-Control-Allow-Headers: content-type, origin, accept, x-app-sig"); $acrh = explode(',', strtolower($headers['Access-Control-Request-Headers'])); foreach ($acrh as $k => $v) { $acrh[$k] = trim($v); } if (! isset($headers['Access-Control-Request-Headers']) || ! in_array('x-app-sig', $acrh)) { _log($h, '*** Bad preflight!' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true)); header("HTTP/1.1 401 Unauthorized"); exit; //-> } _log($h, '+++ Successful preflight.' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true)); exit; //-> } //Now we are past preflight. Actual Auth happens here, I check a signature that I post with payload.
Обновление: ОК, думаю, я лучше пойму ваш вопрос сейчас. Добавлено немного больше кода. Во-первых, да, мы делаем по существу то же самое. Я просто проверяю, что предполетный пробовал пытаться белым списком, что он должен иметь в терминах заголовков.
Я думаю, что часть, которую вам не хватает, заключается в том, что предполетная команда должна / не иметь настраиваемый заголовок, который вы пытаетесь отправить. См. Ответ здесь: Как вы отправляете собственный заголовок в междоменном (CORS) XMLHttpRequest? ). Так что, как и я, вы можете проверить, что Access-Control-Request-Headers:
отправлены с предполетью, но вы не должны проверять фактический заголовок, присутствующий в этом вызове.
Похоже, вам просто нужно немного переместить код на стороне сервера – сделайте предполетный вид довольно ванильным и немым, а затем сделайте фактическое аутентификацию или проверку пользовательских заголовков после успешного предполета.
Я использую подпись HMAC, отправленную с полезной нагрузкой, для проверки подлинности вещей после предполета. Я также проверю, что пользовательский x-app-sig поставляется и что я ожидаю, хотя это, вероятно, избыточно.
Я дуэлировал этот вопрос в течение двух дней. Выходит, в конце концов, должен быть поток для этих заявок, и это просто невозможно. Сначала вы должны разрешить поля заголовка, которые отправляются (со всеми заголовками CORS), в моем случае это было:
<?php header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, GET, OPTIONS'); header('Access-Control-Allow-Headers: authorization, content-type, x-requested-with');
Затем мне просто приходилось возвращать статус «204 без содержимого» всякий раз, когда пришел запрос OPTIONS. Вы можете использовать оператор условия следующим образом:
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header('HTTP/1.1 204 No Content'); }