Использование Fetch API вместо Ajax для запросов Allow-Credentials + POST

avatar
Lux
7 апреля 2018 в 22:43
877
1
0

В этом сценарии используется метод Access-Control-Allow-Credentials вместе с методом POST для управления переменными сеанса PHP на стороне сервера, которые должны оставаться нетронутыми.

Для справки: внешний интерфейс — это проект create-react-app, работающий по адресу http://localhost:3000, а внутренний — PHP, работающий по адресу example.com.

Достичь этого с помощью метода $.ajax() легко и просто.

  UseAjax(incomingData) {
    return new Promise(function(resolve, reject) {
      $.ajax({
        url: 'http://example.com/api.php',
        type: 'post',
        data: incomingData,
        xhrFields: { withCredentials: true },
        success: function(data) {
          console.log(data)
        }
      })
      .then((data,status) => {
        // Get the result and transform into valid JSON
        if ( typeof data === typeof 'str' ) {
          try {
            data = JSON.parse(data);
          } catch(e) {
            reject(data,status);
            console.log('Exception: ', e);
            console.log('API Returned non-JSON result: ', data);
          }
        }
        return data;
      }).then((dataObject) => {
        console.log('dataObject:');
        console.log(dataObject);
        resolve(dataObject);
      });
    });
  }

Как ни странно, при использовании API fetch() создается впечатление, что я не разрешаю CORS. Конечно, я включил CORS, поскольку этот запрос отлично работает с Ajax и дает сбой только при использовании API fetch().

Вот что я пробовал при использовании fetch() API.

  UseFetch(requestData) {
    return new Promise(function(resolve, reject) {
      console.log('Relay() called with data: ', requestData);
      fetch('http://example.com/api.php', {
        method: 'POST', // or 'PUT'
        body: JSON.stringify(requestData), // data can be `string` or {object}!
        headers: new Headers({
          'Content-Type': 'application/json'
        })
      }).then((result) => {
        // Get the result
        return result.json();
      }).then((jsonResult) => {
        // Do something with the result
        if ( jsonResult.err )
          reject(jsonResult);
        console.log(jsonResult);
        resolve(jsonResult);
      });
    });
  }

Выдает эту ошибку.

Failed to load http://example.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

На стороне PHP я использую простой вывод, чтобы убедиться, что ничего не происходит неправильно, вызывая ошибку на стороне сервера.

<?php
  header('Content-Type: application/json');
  header('Access-Control-Allow-Origin: http://example.com');
  header('Access-Control-Allow-Methods: POST');
  header('Access-Control-Allow-Headers: Content-Type, Authorization, x-requested-with');
  echo json_encode(['data'=>'result']);
?>

Я следил за многими вопросами, но в первую очередь этот вопрос с очень подробным объяснением проблемы и возможных решений.

На данный момент я просто использую проверенный и надежный $.ajax() для выполнения этой задачи, но я хочу полностью понять API fetch() в той мере, в какой это необходимо для воспроизведения этой функциональности, поскольку это кажется чем-то очень базовый из моего опыта.

Источник
sideshowbarker
9 апреля 2018 в 05:40
0

Вы, вероятно, захотите использовать панель «Сеть» инструментов разработчика вашего браузера, чтобы просмотреть полную информацию о запросе и ответе для обоих случаев (случай $.ajax() и случай fetch()), включая полные заголовки запроса и метод запроса, а также заголовки ответа и код состояния HTTP ответа, а затем используйте coderhelper.com/posts/49712690/edit, чтобы вставить эти данные в вопрос.

Kevin B
9 апреля 2018 в 17:35
0

Между вашим кодом $.ajax и кодом выборки есть одно различие: в коде выборки вы добавляете дополнительный заголовок ContentType, который не был установлен кодом $.ajax, что заставит браузер отправить предварительный запрос. . Ваш php-код, похоже, не написан таким образом, чтобы правильно обрабатывать предварительную проверку. Однако .... ваше сообщение об ошибке не совсем соответствует этой проблеме. В сообщении об ошибке говорится, что не было заголовка управления доступом, что может быть связано с ошибкой PHP, в результате чего на вашей стандартной странице с ошибкой 500 нет заголовков CORS.

Ответы (1)

avatar
roryhewitt
9 апреля 2018 в 17:30
-2

В вашем коде PHP вы указали header('Access-Control-Allow-Origin: http://example.com');. Это неправильно - вам нужно указать значение, переданное в заголовке запроса Origin (которое в вашем случае будет http://localhost:3000):

header('Access-Control-Allow-Origin: http://localhost:3000');

В идеале вы не должны жестко кодировать его — вы просто извлекаете заголовок запроса Origin и передаете его обратно.

Кстати, я полагаю, что JQuery $.Ajax использует fetch() скрыто (точно так же, как XmlHTTPRequest()). В основном все использует выборку...

Kevin B
9 апреля 2018 в 17:41
0

учитывая полученное сообщение об ошибке, я не думаю, что это текущая проблема, но потенциально она может возникнуть после того, как текущая проблема будет решена. Текущее сообщение об ошибке предполагает отсутствие заголовка управления доступом. Если бы он был, но источник не совпадал, сообщение об ошибке было бы другим.

roryhewitt
9 апреля 2018 в 17:46
0

Как предлагает @sideshowbarker в своем комментарии к вашему исходному сообщению, можете ли вы предоставить полный набор заголовков запросов и ответов? А, я вижу, ты не ОП. Извинения.

Barkermn01
11 сентября 2018 в 09:53
0

Вы просто ошибаетесь насчет jQuery api.jquery.com/Types/#jqXHR, он построен на основе XMLHttpRequest, Fetch — это совершенно новый API, построенный на базовом уровне. Оба они предназначены для обработки запросов, построенных на TCP/IP с использованием стандартов HTTPS или HTTP. но это код, созданный на C++, при их использовании они полностью изолированы и не зависят друг от друга, и поскольку XMLHttpRequest был первым. Если бы они собирались строить поверх него, они бы строили поверх этого, а не перестраивали XMLHttpRequest для использования кода API выборки. это просто пустая трата времени.

roryhewitt
11 сентября 2018 в 23:39
0

Мартин, вы правы в том, что выборка и XMLHttpRequest разные. В этом я ошибался — по какой-то причине я думал, что XHR был перестроен для использования выборки. Тем не менее, кажется немного резким отрицать мой (в противном случае, возможно, правильный) ответ, не так ли? Даже если он не отвечает на всю проблему, факт в том, что заголовки ответов закодированы неправильно. Вздох.